准备工作
在正式编写代码实现联系人管理模块中分页查询联系人列表的功能之前,我们得做一些准备工作,因为这个功能实现起来还是比较难的,不能一蹴而就。
创建联系人表
在crm数据库下新建一张联系人表,其建表的sql语句如下:
CREATE TABLE `cst_linkman` (
`lkm_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '联系人编号(主键)',
`lkm_name` varchar(16) DEFAULT NULL COMMENT '联系人姓名',
`lkm_cust_id` bigint(32) NOT NULL COMMENT '客户id',
`lkm_gender` char(1) DEFAULT NULL COMMENT '联系人性别',
`lkm_phone` varchar(16) DEFAULT NULL COMMENT '联系人办公电话',
`lkm_mobile` varchar(16) DEFAULT NULL COMMENT '联系人手机',
`lkm_email` varchar(64) DEFAULT NULL COMMENT '联系人邮箱',
`lkm_qq` varchar(16) DEFAULT NULL COMMENT '联系人qq',
`lkm_position` varchar(16) DEFAULT NULL COMMENT '联系人职位',
`lkm_memo` varchar(512) DEFAULT NULL COMMENT '联系人备注',
PRIMARY KEY (`lkm_id`),
KEY `FK_cst_linkman_lkm_cust_id` (`lkm_cust_id`),
CONSTRAINT `FK_cst_linkman_lkm_cust_id` FOREIGN KEY (`lkm_cust_id`) REFERENCES `cst_customer` (`cust_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
创建LinkMan实体类及其对应的映射配置文件
首先,在com.meimeixia.crm.domain包下创建一个LinkMan实体类及其相对应的映射配置文件。
-
LinkMan实体类
package com.meimeixia.crm.domain; public class LinkMan { private Long lkm_id; private String lkm_name; private String lkm_gender; private String lkm_phone; private String lkm_mobile; private String lkm_email; private String lkm_qq; private String lkm_position; private String lkm_memo; //客户是一的一方,联系人是多的一方。在联系人这一方放置客户的对象 private Customer customer; public Long getLkm_id() { return lkm_id; } public void setLkm_id(Long lkm_id) { this.lkm_id = lkm_id; } public String getLkm_name() { return lkm_name; } public void setLkm_name(String lkm_name) { this.lkm_name = lkm_name; } public String getLkm_gender() { return lkm_gender; } public void setLkm_gender(String lkm_gender) { this.lkm_gender = lkm_gender; } public String getLkm_phone() { return lkm_phone; } public void setLkm_phone(String lkm_phone) { this.lkm_phone = lkm_phone; } public String getLkm_mobile() { return lkm_mobile; } public void setLkm_mobile(String lkm_mobile) { this.lkm_mobile = lkm_mobile; } public String getLkm_email() { return lkm_email; } public void setLkm_email(String lkm_email) { this.lkm_email = lkm_email; } public String getLkm_qq() { return lkm_qq; } public void setLkm_qq(String lkm_qq) { this.lkm_qq = lkm_qq; } public String getLkm_position() { return lkm_position; } public void setLkm_position(String lkm_position) { this.lkm_position = lkm_position; } public String getLkm_memo() { return lkm_memo; } public void setLkm_memo(String lkm_memo) { this.lkm_memo = lkm_memo; } public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } }
客户和联系人它俩之间的关系是一对多的关系,那么在创建联系人实体类时,要不要在这一端写上一的一方的对象呢?需要,因为我们在查询联系人表中的数据时,需要关联查询客户表中的数据(例如客户名称)。
-
LinkMan.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> <!-- 建立类与表的映射 --> <class name="com.meimeixia.crm.domain.LinkMan" table="cst_linkman"> <!-- 建立类中的属性与表中的主键相对应 --> <id name="lkm_id" column="lkm_id"> <!-- 主键的生成策略,现在使用的是本地生成策略 --> <generator class="native" /> </id> <!-- 建立类中的普通属性和表中的字段相对应 --> <property name="lkm_name" column="lkm_name" /> <property name="lkm_gender" column="lkm_gender" /> <property name="lkm_phone" column="lkm_phone" /> <property name="lkm_mobile" column="lkm_mobile" /> <property name="lkm_email" column="lkm_email" /> <property name="lkm_qq" column="lkm_qq" /> <property name="lkm_position" column="lkm_position" /> <property name="lkm_memo" column="lkm_memo" /> <!-- 配置联系人与客户的多对一的映射 --> <many-to-one name="customer" class="com.meimeixia.crm.domain.Customer" column="lkm_cust_id"></many-to-one> </class> </hibernate-mapping>
在创建联系人类相对应的映射配置文件时,需要配置联系人与客户的多对一的映射。
然后,我们就要修改Customer实体类及其对应的映射配置文件了。
-
先修改了客户实体类,因为客户和联系人它俩之间是属于多对一的关系,所以我们需要在一的一方放置多的一方的集合。为什么要有这个集合呢?因为如果你想做级联删除什么的话,那么这个集合就必须得有。
package com.meimeixia.crm.domain; import java.util.HashSet; import java.util.Set; public class Customer { private Long cust_id; private String cust_name; /*private String cust_source; private String cust_industry; private String cust_level;*/ private String cust_phone; private String cust_mobile; /* * 因为我们的客户表和数据字典表是属于多对一的关系,所以需要在多的一方放置一的一方的对象 */ private BaseDict baseDictSource; private BaseDict baseDictIndustry; private BaseDict baseDictLevel; private String cust_image;//客户资质的图片 //一个客户可以有多个联系人 //为什么要有这个集合呢?因为你想做级联删除什么的,这个集合就必须得有 private Set<LinkMan> linkMans = new HashSet<LinkMan>();//双向关联配置 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; } public void setCust_name(String cust_name) { this.cust_name = cust_name; } 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 getBaseDictSource() { return baseDictSource; } public void setBaseDictSource(BaseDict baseDictSource) { this.baseDictSource = baseDictSource; } public BaseDict getBaseDictIndustry() { return baseDictIndustry; } public void setBaseDictIndustry(BaseDict baseDictIndustry) { this.baseDictIndustry = baseDictIndustry; } public BaseDict getBaseDictLevel() { return baseDictLevel; } public void setBaseDictLevel(BaseDict baseDictLevel) { this.baseDictLevel = baseDictLevel; } public String getCust_image() { return cust_image; } public void setCust_image(String cust_image) { this.cust_image = cust_image; } public Set<LinkMan> getLinkMans() { return linkMans; } public void setLinkMans(Set<LinkMan> linkMans) { this.linkMans = linkMans; } }
-
再修改与客户实体类相对应的映射配置文件。
<?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> <!-- 建立类与表的映射 --> <class name="com.meimeixia.crm.domain.Customer" table="cst_customer"> <!-- 建立类中的属性与表中的主键相对应 --> <id name="cust_id" column="cust_id"> <!-- 主键的生成策略,现在使用的是本地生成策略 --> <generator class="native" /> </id> <!-- 建立类中的普通属性和表中的字段相对应 --> <property name="cust_name" column="cust_name" /> <!-- <property name="cust_source" column="cust_source" /> <property name="cust_industry" column="cust_industry" /> <property name="cust_level" column="cust_level" /> --> <property name="cust_phone" column="cust_phone" /> <property name="cust_mobile" column="cust_mobile" /> <property name="cust_image" column="cust_image" /> <!-- 配置客户与数据字典它哥俩之间的多对一的关系映射 --> <many-to-one name="baseDictSource" class="com.meimeixia.crm.domain.BaseDict" column="cust_source"></many-to-one> <many-to-one name="baseDictIndustry" class="com.meimeixia.crm.domain.BaseDict" column="cust_industry"></many-to-one> <many-to-one name="baseDictLevel" class="com.meimeixia.crm.domain.BaseDict" column="cust_level"></many-to-one> <!-- 配置与联系人的关系映射 --> <set name="linkMans"> <key column="lkm_cust_id"></key> <one-to-many class="com.meimeixia.crm.domain.LinkMan" /> </set> </class> </hibernate-mapping>
最后,千万记得要把联系人实体类相对应的映射配置文件交给Spring来管理哟!
创建相关的类(接口)
首先创建web层相关的类,即在com.meimeixia.crm.web.action包下创建一个LinkManAction类,但仅在其中注入service。
package com.meimeixia.crm.web.action;
import com.meimeixia.crm.domain.LinkMan;
import com.meimeixia.crm.service.LinkManService;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
/**
* 联系人的Action类
* @author liayun
*
*/
public class LinkManAction extends ActionSupport implements ModelDriven<LinkMan> {
//模型驱动使用的对象
private LinkMan linkMan = new LinkMan();
@Override
public LinkMan getModel() {
return linkMan;
}
//注入service
private LinkManService linkManService;
public void setLinkManService(LinkManService linkManService) {
this.linkManService = linkManService;
}
}
然后,创建service层相关的类。
-
先在com.meimeixia.crm.service包下创建一个LinkManService接口,一开始该接口中并没有声明任何方法,如下:
package com.meimeixia.crm.service; /** * 联系人的业务层的接口 * @author liayun * */ public interface LinkManService { }
-
再在com.meimeixia.crm.service.impl包下编写以上接口的一个实现类——LinkManServiceImpl.java,但仅在其中注入dao。
package com.meimeixia.crm.service.impl; import com.meimeixia.crm.dao.LinkManDao; import com.meimeixia.crm.service.LinkManService; /** * 联系人的业务层的实现类 * @author liayun * */ public class LinkManServiceImpl implements LinkManService { //注入dao private LinkManDao linkManDao; public void setLinkManDao(LinkManDao linkManDao) { this.linkManDao = linkManDao; } }
接着,创建dao层相关的类。
-
先在com.meimeixia.crm.dao包下创建一个LinkManDao接口,一开始该接口中并没有声明任何方法,如下:
package com.meimeixia.crm.dao; /** * 联系人的dao的接口 * @author liayun * */ public interface LinkManDao { }
-
再在com.meimeixia.crm.dao.impl包下编写以上接口的一个实现类——LinkManDaoImpl.java。
package com.meimeixia.crm.dao.impl; import org.springframework.orm.hibernate5.support.HibernateDaoSupport; import com.meimeixia.crm.dao.LinkManDao; /** * 联系人的dao的实现类 * @author liayun * */ public class LinkManDaoImpl extends HibernateDaoSupport implements LinkManDao { }
最后,我们不要忘了在Spring配置文件中对以上类进行配置,即将这些相关的类交给Spring来管理。
编写代码实现分页查询联系人列表的功能
在左侧的菜单页面(menu.jsp)中修改提交路径
编写web层
首先,我们要在LinkManAction类中编写一个分页查询联系人列表的方法。在该方法中,需要接收分页查询的参数,这里最好使用离线条件查询对象(即DetachedCriteria对象),因为用了它之后,在我们底层Hibernate模板调用的时候,直接就可以进行分页查询了,而且后期咱们进行条件查询(条件查询还能带分页),使用DetachedCriteria这个对象就会变得非常方便。
package com.meimeixia.crm.web.action;
import org.hibernate.criterion.DetachedCriteria;
import com.meimeixia.crm.domain.LinkMan;
import com.meimeixia.crm.domain.PageBean;
import com.meimeixia.crm.service.LinkManService;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
/**
* 联系人的Action类
* @author liayun
*
*/
public class LinkManAction extends ActionSupport implements ModelDriven<LinkMan> {
//模型驱动使用的对象
private LinkMan linkMan = new LinkMan();
@Override
public LinkMan getModel() {
return linkMan;
}
//注入service
private LinkManService linkManService;
public void setLinkManService(LinkManService linkManService) {
this.linkManService = linkManService;
}
//接收如下分页参数:
private Integer currentPage = 1;
private Integer pageSize = 3;
public void setCurrentPage(Integer currentPage) {
if (currentPage == null) {
currentPage = 1;
}
this.currentPage = currentPage;
}
public void setPageSize(Integer pageSize) {
if (pageSize == null) {
pageSize = 3;
}
this.pageSize = pageSize;
}
/*
* 查询联系人列表的方法
*/
public String findAll() {
//创建离线条件查询对象
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(LinkMan.class);
//设置条件,有条件就设置条件,没有条件就直接调用业务层
//...
//调用业务层
PageBean<LinkMan> pageBean = linkManService.findAll(detachedCriteria, currentPage, pageSize);
ActionContext.getContext().getValueStack().push(pageBean);
return "findAll";
}
}
然后,我们还得在Struts2配置文件中(即struts.xml)对LinkManAction进行如下的配置,即配置页面的跳转。
编写service层
首先,在LinkManService接口中添加一个分页查询联系人列表的方法声明,如下:
package com.meimeixia.crm.service;
import org.hibernate.criterion.DetachedCriteria;
import com.meimeixia.crm.domain.LinkMan;
import com.meimeixia.crm.domain.PageBean;
/**
* 联系人的业务层的接口
* @author liayun
*
*/
public interface LinkManService {
PageBean<LinkMan> findAll(DetachedCriteria detachedCriteria, Integer currentPage, Integer pageSize);
}
然后,在以上接口的一个实现类(LinkManServiceImpl.java)中去实现分页查询联系人列表的方法。
package com.meimeixia.crm.service.impl;
import java.util.List;
import org.hibernate.criterion.DetachedCriteria;
import com.meimeixia.crm.dao.LinkManDao;
import com.meimeixia.crm.domain.LinkMan;
import com.meimeixia.crm.domain.PageBean;
import com.meimeixia.crm.service.LinkManService;
/**
* 联系人的业务层的实现类
* @author liayun
*
*/
public class LinkManServiceImpl implements LinkManService {
//注入dao
private LinkManDao linkManDao;
public void setLinkManDao(LinkManDao linkManDao) {
this.linkManDao = linkManDao;
}
//业务层来分页查询联系人的方法
@Override
public PageBean<LinkMan> findAll(DetachedCriteria detachedCriteria, Integer currentPage, Integer pageSize) {
PageBean<LinkMan> pageBean = new PageBean<LinkMan>();
//设置当前页数
pageBean.setCurrentPage(currentPage);
//设置每页显示的记录数
pageBean.setPageSize(pageSize);
//设置总记录数
Integer totalCount = linkManDao.findCount(detachedCriteria);
pageBean.setTotalCount(totalCount);
//设置总页数
double tc = totalCount;
Double num = Math.ceil(tc / pageSize);
pageBean.setTotalPage(num.intValue());
//设置每页显示数据的集合
Integer begin = (currentPage - 1) * pageSize;
List<LinkMan> list = linkManDao.findByPage(detachedCriteria, begin, pageSize);
pageBean.setList(list);
return pageBean;
}
}
编写dao层
首先,在LinkManDao接口中添加一个统计联系人记录数的方法声明和一个分页查询联系人列表的方法声明,如下:
package com.meimeixia.crm.dao;
import java.util.List;
import org.hibernate.criterion.DetachedCriteria;
import com.meimeixia.crm.domain.LinkMan;
/**
* 联系人的dao的接口
* @author liayun
*
*/
public interface LinkManDao {
Integer findCount(DetachedCriteria detachedCriteria);
List<LinkMan> findByPage(DetachedCriteria detachedCriteria, Integer begin, Integer pageSize);
}
然后,在以上接口的一个实现类(LinkManDaoImpl.java)中去实现上面的两个方法。
package com.meimeixia.crm.dao.impl;
import java.util.List;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.springframework.orm.hibernate5.support.HibernateDaoSupport;
import com.meimeixia.crm.dao.LinkManDao;
import com.meimeixia.crm.domain.LinkMan;
/**
* 联系人的dao的实现类
* @author liayun
*
*/
public class LinkManDaoImpl extends HibernateDaoSupport implements LinkManDao {
//dao中统计个数的方法
@Override
public Integer findCount(DetachedCriteria detachedCriteria) {
detachedCriteria.setProjection(Projections.rowCount());
List<Long> list = (List<Long>) this.getHibernateTemplate().findByCriteria(detachedCriteria);
if (list.size() > 0) {
return list.get(0).intValue();
}
return null;
}
//dao中分页查询联系人的方法
@Override
public List<LinkMan> findByPage(DetachedCriteria detachedCriteria, Integer begin, Integer pageSize) {
//首先要把count(*)语句给清空掉
detachedCriteria.setProjection(null);
List<LinkMan> list = (List<LinkMan>) this.getHibernateTemplate().findByCriteria(detachedCriteria, begin, pageSize);
return list;
}
}
在联系人列表页面中显示数据
先不要急着去实现页面底部的分页工具条,仅仅是在页面中显示一下查询出来的联系人列表数据。
此时,发布我们的项目到Tomcat服务器并启动,然后访问该项目的首页,点击联系人列表超链接之后就能正确地显示联系人列表了。
可以看到在显示联系人对象所关联的客户对象时,并不会出现延迟加载的问题,这是因为我们在web.xml文件配置了OpenSessionInViewFilter过滤器,它就解决了延迟加载的问题。
接下来,我们就来实现页面底部的分页工具条,写起来确实很麻烦,但在CRM客户关系管理系统开发的第五讲中,我就已经写出来了页面底部的分页工具条的实现代码,因此,我们在这儿可以复用这段代码。
再次发布我们的项目到Tomcat服务器并启动,然后访问该项目的首页,点击联系人列表超链接之后,你就能进行测试了,应该是好使的!