crm项目 客户管理 > 添加客户
背景:添加客户时有一些值是固定的选项,比如:客户信息来源(电话营销、网络营销),客户级别(VIP客户、普通客户)。
在添加客户时,这些可以通过下拉框的形式来选择,防止自行填写时出现不应有的数据,如:客户级别只有vip、普通两种,自行填写时有可能会填一个“至尊”,这就有悖初衷。下拉框中的选项如果直接在显示层(jsp)写死,不便于更改,不够灵活,所以采用ajax技术动态的从数据库中获取,然后动态的添加下拉框选项。
1. 数据库实现
cst_customer表中的cust_level、cust_source、cust_industry字段均引用base_dict表的dict_id字段做外键。
表名:base_dict 描述:数据字典表
表名:cst_customer 描述:客户信息表
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. 访问流程图
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格式发送到浏览器
ListCustomerAction.java
用模型驱动封装好的customer对象的cust_name值作为条件封装离线查询对象。
调用Service层的getPageBean(DetachedCriteria dc, Integer currentPage, Integer pageSize)方法封装pageBean对象,PageBean类详情放到了最后。将封装好的pageBean对象方法request域中,以便list.jsp使用。
list.jsp
使用ognl表达式从request域中取出pageBean.list集合,定义别名为customer。
以为cust_level、cust_source是对象类型,我们要取的是其中的dict_tz_name属性
注意!!!
在封装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>