天天看点

CRM项目(数据字典+ajax+动态下拉框)

crm项目  客户管理 > 添加客户

背景:添加客户时有一些值是固定的选项,比如:客户信息来源(电话营销、网络营销),客户级别(VIP客户、普通客户)。

在添加客户时,这些可以通过下拉框的形式来选择,防止自行填写时出现不应有的数据,如:客户级别只有vip、普通两种,自行填写时有可能会填一个“至尊”,这就有悖初衷。下拉框中的选项如果直接在显示层(jsp)写死,不便于更改,不够灵活,所以采用ajax技术动态的从数据库中获取,然后动态的添加下拉框选项。

1. 数据库实现

cst_customer表中的cust_level、cust_source、cust_industry字段均引用base_dict表的dict_id字段做外键。

表名:base_dict                  描述:数据字典表

CRM项目(数据字典+ajax+动态下拉框)

表名:cst_customer            描述:客户信息表

CRM项目(数据字典+ajax+动态下拉框)

2. 实体类

/**
 * 
 * @author Aha
 * 客户实体类
 *
 */
public class Customer {
	private Long cust_id; // ID
	
	private String cust_name; // 客户名称
	private String cust_linkman; // 联系人
	private String cust_phone; // 固定电话
	private String cust_mobile; // 移动电话
	
	/**
	 * 数据字典类型
	 */
	private BaseDict cust_source; // 信息来源
	private BaseDict cust_level; // 客户级别
	private BaseDict cust_industry; // 所属行业
	
	public Long getCust_id() {
		return cust_id;
	}
	public void setCust_id(Long cust_id) {
		this.cust_id = cust_id;
	}
	public String getCust_name() {
		return cust_name;
	}
	@Required
	public void setCust_name(String cust_name) {
		this.cust_name = cust_name;
	}
	public String getCust_linkman() {
		return cust_linkman;
	}
	public void setCust_linkman(String cust_linkman) {
		this.cust_linkman = cust_linkman;
	}
	public String getCust_phone() {
		return cust_phone;
	}
	public void setCust_phone(String cust_phone) {
		this.cust_phone = cust_phone;
	}
	public String getCust_mobile() {
		return cust_mobile;
	}
	public void setCust_mobile(String cust_mobile) {
		this.cust_mobile = cust_mobile;
	}
	public BaseDict getCust_source() {
		return cust_source;
	}
	public void setCust_source(BaseDict cust_source) {
		this.cust_source = cust_source;
	}
	public BaseDict getCust_level() {
		return cust_level;
	}
	public void setCust_level(BaseDict cust_level) {
		this.cust_level = cust_level;
	}
	public BaseDict getCust_industry() {
		return cust_industry;
	}
	public void setCust_industry(BaseDict cust_industry) {
		this.cust_industry = cust_industry;
	}
	
	@Override
	public String toString() {
		return "Customer [cust_id=" + cust_id + ", cust_name=" + cust_name + "]";
	}
}
           
/**
 * 
 * @author Aha
 * 客户实体类
 *
 */
public class Customer {
	private Long cust_id; // ID
	
	private String cust_name; // 客户名称
	private String cust_linkman; // 联系人
	private String cust_phone; // 固定电话
	private String cust_mobile; // 移动电话
	private Set<LinkMan> linkMans; // 联系人集合
	/**
	 * 数据字典类型
	 */
	private BaseDict cust_source; // 信息来源
	private BaseDict cust_level; // 客户级别
	private BaseDict cust_industry; // 所属行业
	
	public Long getCust_id() {
		return cust_id;
	}
	public void setCust_id(Long cust_id) {
		this.cust_id = cust_id;
	}
	public String getCust_name() {
		return cust_name;
	}
	@Required
	public void setCust_name(String cust_name) {
		this.cust_name = cust_name;
	}
	public String getCust_linkman() {
		return cust_linkman;
	}
	public void setCust_linkman(String cust_linkman) {
		this.cust_linkman = cust_linkman;
	}
	public String getCust_phone() {
		return cust_phone;
	}
	public void setCust_phone(String cust_phone) {
		this.cust_phone = cust_phone;
	}
	public String getCust_mobile() {
		return cust_mobile;
	}
	public void setCust_mobile(String cust_mobile) {
		this.cust_mobile = cust_mobile;
	}
	public BaseDict getCust_source() {
		return cust_source;
	}
	public void setCust_source(BaseDict cust_source) {
		this.cust_source = cust_source;
	}
	public BaseDict getCust_level() {
		return cust_level;
	}
	public void setCust_level(BaseDict cust_level) {
		this.cust_level = cust_level;
	}
	public BaseDict getCust_industry() {
		return cust_industry;
	}
	public void setCust_industry(BaseDict cust_industry) {
		this.cust_industry = cust_industry;
	}
	public Set<LinkMan> getLinkMans() {
		return linkMans;
	}
	public void setLinkMans(Set<LinkMan> linkMans) {
		this.linkMans = linkMans;
	}
	@Override
	public String toString() {
		return "Customer [cust_id=" + cust_id + ", cust_name=" + cust_name + "]";
	}
}
           

3. 实体类映射配置文件

① Customer.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
   <!-- 配置表与实体对象的关系 -->
   <!-- package属性:填写一个包名.在元素内部凡是需要书写完整类名的属性,可以直接写简答类名了. -->
<hibernate-mapping package="com.sdyu.domain" >
	<class name="Customer" table="cst_customer" >
		<!-- id、property元素:配置主键映射的属性
				name: 填写主键对应属性名
				column(可选): 填写表中的主键列名.默认值:列名会默认使用属性名
				type(可选):填写列(属性)的类型.hibernate会自动检测实体的属性类型.
						每个类型有三种填法: java类型|hibernate类型|数据库类型
				not-null(可选):配置该属性(列)是否不能为空. 默认值:false
				length(可选):配置数据库中列的长度. 默认值:使用数据库类型的最大长度
		 -->
		<id name="cust_id"  >
			<!-- generator:主键生成策略 -->
			<generator class="native"></generator>
		</id>
		<property name="cust_name" column="cust_name" ></property>
		<property name="cust_linkman" column="cust_linkman" ></property>
		<property name="cust_phone" column="cust_phone" ></property>
		<property name="cust_mobile" column="cust_mobile" ></property>
		
		<set name="linkMans">
			<key column="lkm_cust_id"></key>
			<one-to-many class="LinkMan" />
		</set>
		
		<many-to-one name="cust_source" column="cust_source" class="com.sdyu.domain.BaseDict"></many-to-one>
		<many-to-one name="cust_level" column="cust_level" class="com.sdyu.domain.BaseDict"></many-to-one>
		<many-to-one name="cust_industry" column="cust_industry" class="com.sdyu.domain.BaseDict"></many-to-one>
		
	</class>
</hibernate-mapping>
           

② BaseDict.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    
    <hibernate-mapping package="com.sdyu.domain">
    	<class name="BaseDict" table="base_dict">
    		<id name="dict_id">
    			<generator class="uuid"></generator>
    		</id>
    		<property name="dict_type_code"></property>
    		<property name="dict_type_name"></property>
    		<property name="dict_tz_name"></property>
    		<property name="dict_tz_code"></property>
    		<property name="dict_sort"></property>
    		<property name="dict_enable"></property>
    		<property name="dict_memo"></property>
    	</class>
    </hibernate-mapping>
           

关系的维护只放在Customer这一方,BaseDict放弃维护。因为在使用时,只会通过Customer查询BaseDict中的数据,而不会通过BaseDict查询Customer,所以在BaseDict中就没有维护关系的必要。

4. 访问流程图

CRM项目(数据字典+ajax+动态下拉框)

5. add.jsp中的ajax代码

<!-- query方式 -->
<script type="text/javascript">	
	function loadSelect(typeCode,selectId,selectedId){
		//1.创建select对象,
		var $select = $("#" + selectId);
		//2.添加显示选项
		$select.append($("<option value=''>- - - 请 选 择 - - -</option>"));
		
		//3.使用jquery的ajax方法,访问后台action
		$.post("${pageContext.request.contextPath}/baseDictAction", {dictTypeCode : typeCode},
		 	 function(data){
			//第一个为对象的成员或数组的索引,第二个为对应变量或内容
			console.log(data);
	 		//4.返回json数组对象,对其遍历
			$.each( data, function(i, json){
				console.log(json);
				//alert(json['dict_tz_name']);
	 			//5.将每次遍历的数据添加到option ,先创建一个option
				var $option = $("<option value='"+json['dict_id']+"'>"+json['dict_tz_name']+"</option>");
	 			
	 			//判断是否需要回显,如果需要使其被选中
	 			if(json['dict_id'] == selectedId){
	 				$option.attr("selected","selected");
	 			}
	 			//5.并添加到select对象中
				$select.append($option);
	 		});
		},"json");
		//将拼接好的select对象放入到页面指定的位置
	}
	
	//页面加载完毕
	$(function(){
		loadSelect("6","level",'cust_level.dict_id');	
		loadSelect("2","source","cust_source.dict_id");	
		loadSelect("1","industry","cust_industry.dict_id");		
	});
</script>
           
<!-- javascript方式(穿插jquery) -->
function loadSelect(typeCode, id) {
		var xhr = new XMLHttpRequest();
		xhr.open("POST", "ssh_crm_final/baseDictAction", false);
		xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
		xhr.onreadystatechange = function() {
			if (xhr.readyState == 4 && xhr.status == 200) {
				var json = JSON.parse(xhr.responseText);
				$("#"+id).empty();
				$("#"+id).append("<option>- - - 请选择   - - -</option>");
				for (var i in json) {
					var option = document.createElement("option");
					option.text = json[i].dict_tz_name;
					option.value = json[i].dict_id;
					$("#"+id).append(option);
				}
			}
		}
		xhr.send("dictTypeCode="+typeCode);
	}
	$(function(){
		loadSelect("6","level");	
		loadSelect("2","source");	
		loadSelect("1","industry");	
	 });
           

在使用javascript方式的ajax请求获取数据时,遇到了数据混乱问题。即,当请求设为异步的时候,拿到的数据有可能是三个重复的等等,这时候看console打印台,传过来的dict_type_code参数值是重复的,所以获取的数据是相同的;当请求设为同步时,则不会出现这种情况,但是又有些违背了ajax异步处理的初衷。

6. 处理流程关键部分代码

BaseDictAction.java

根据ajax请求发送过来的dictTypeCode参数查询数据库,得到对应的数据字典集合。转换成json格式发送到浏览器

CRM项目(数据字典+ajax+动态下拉框)

ListCustomerAction.java

用模型驱动封装好的customer对象的cust_name值作为条件封装离线查询对象。

调用Service层的getPageBean(DetachedCriteria dc, Integer currentPage, Integer pageSize)方法封装pageBean对象,PageBean类详情放到了最后。将封装好的pageBean对象方法request域中,以便list.jsp使用。

CRM项目(数据字典+ajax+动态下拉框)
CRM项目(数据字典+ajax+动态下拉框)

list.jsp

使用ognl表达式从request域中取出pageBean.list集合,定义别名为customer。

以为cust_level、cust_source是对象类型,我们要取的是其中的dict_tz_name属性

CRM项目(数据字典+ajax+动态下拉框)

注意!!!

在封装pageBean时,其中list中customer对象的cust_level、cust_source、cust_indutory是代理对象(debug时 $ $ 为代理对象),里面的值都是null,这里涉及到了hibernate的延迟加载策略(懒加载),即:在查询的时候并不会一次性将所有的都查询处理,而是用到的时候才会发送sql语句进行查询。所以,知道在list.jsp中使用ognl表达式取出pageBean.list时,才发送sql语句进行查询,在这之前list中的customer的cust_level、cust_source、cust_indutory都为代理对象。

加载策略默认为:   lazy="true"; fetch="join";   单表延迟加载

可以在配置文件Customer.hbm.xml中进行配置。加上fetch="join"属性,即加载策略该为多表查询,这时无论lazy的取值如何,都是立即加载,在封装pageBean对象时查询custmoer对象时会顺带将其中涉及到的关联关系表(即Customer对象的cust_level,cust_source,cust_industry属性)一起查询,则不再是代理对象,其中也有了数据。

<many-to-one name="cust_industry" fetch="join" column="cust_industry" class="BaseDict"></many-to-one>
<many-to-one name="cust_level" fetch="join"  column="cust_level" class="BaseDict"></many-to-one>
<many-to-one name="cust_source" fetch="join"  column="cust_source" class="BaseDict"></many-to-one>
           

继续阅读