CRM练习介绍
crm:customer relational manager,客户关系管理
功能模块划分
第一个 客户管理模块
(1)新增客户功能
(2)客户列表功能
(3)修改客户功能
(4)删除客户功能
(5)分页列表功能
第二个 联系人管理模块
(1)新增联系人
- 选择所属客户
- 上传文件(struts2实现上传)
(2)联系人列表
(3)修改联系人
(4)级联删除(删除客户,把客户里面所有联系人也删除)
第三个 客户拜访管理模块
(1)新增客户拜访
(2)客户拜访列表
- 解决问题:hibernate多对多配置实现方式
第四个 综合查询模块
(1)客户条件查询
- 多条件组合查询
第五个 统计分析模块
(1)根据来源进行统计分析
- 使用hibernateTemplate调用底层sql
4 练习使用技术:
Struts2 + hibernate5.x + spring4.x + mysql数据库
环境搭建
第一步 导入jar包
第二步 搭建struts2环境
(1)创建action,创建struts.xml配置文件,配置action
(2)配置struts2的过滤器
第三步 搭建hibernate环境
(1)创建实体类
(2)配置实体类和数据库表映射关系
(3)创建hibernate核心配置文件
- 引入映射配置文件
第四步 搭建spring环境
(1)创建spring核心配置文件
(2)让spring配置文件在服务器启动时候加载
- 配置监听器
- 指定spring配置文件位置
第五步 struts2和spring整合
(1)把action在spring配置(action多实例的)
(2)在struts.xml中action标签class属性里面写 bean的id值
第六步 spring和hibernate整合
(1)把hibernate核心配置文件中数据库配置,在spring里面配置
(2)把hibernate的sessionFactory在spring配置
第七步 完成互相注入关系(在dao里面使用hibernateTemplate)
(1)在dao注入hibernateTemplate对象 (extends HibernateDaoSupport)
(2)在hibernateTemplate对象中注入sessionFactory
第八步 配置事务
技术点_知识点_
一、Hibernate模板方法
• Serializable save(Object entity)
• void update(Object entity)
• void delete(Object entity)
• <T> T get(Class<T> entityClass, Serializable id)
• List find(String queryString, Object... values)
删除、修改的规范写法: 先查询操作,后删除操作。
可使用模型驱动封装得到页面id值
修改时通常根据id值修改信息,则需要将id值作为隐藏输入项,在jsp页面中传向action
删除示例
二、分页
/**
* 由于分页操作中,所需数据值较多,所以将数据值统一封装成pageBean. 便于存储,传递。
*
* //在service中完成对pageBean的封装,在dao中使用封装数据进行查询
* @author 一万年行不行
*
*/
public class PageBean {
private Integer begin; //起始位置
private Integer pageSize; //每页记录数
private Integer currentPage; //当前页
private Integer totalCount; //总记录数
private Integer totalPage; //总页数
private List<Customer> list; //每页记录集合
}
pagebean封装过程
//封装数据到pagebean里面
public PageBean findPage(Integer currentPage) {
PageBean pageBean = new PageBean();
//当前页
pageBean.setCurrentPage(currentPage);
//总记录数
//查询数据库得到总记录数
int totalCount = customerDao.findCount();
pageBean.setTotalCount(totalCount);
int pageSize = 3;
//总页数
// -- 总记录数除以每页显示记录数,
// 如果能够整除,结果是相除结果
// 如果不能整除,结果是相除的结果+1
int totalPage = 0;
if(totalCount%pageSize==0) {//整除
totalPage = totalCount/pageSize;
} else {
totalPage = totalCount/pageSize+1;
}
pageBean.setTotalPage(totalPage);
//每页查询list集合
//计算开始位置
int begin = (currentPage-1)*pageSize;
List<Customer> list = customerDao.findAllPage(begin,pageSize);
pageBean.setList(list);
return pageBean;
}
分页查询两种方式
JSP页面
三、条件查询
四、一对多配置
五、struts2文件上传
/**
* 表单页面有要求
第一个要求:表单提交方式 post
第二个要求:form标签里面有属性 enctype属性值 修改为 multipart/form-data
第三个要求:在表单里面有文件上传项,有name属性 <input type= “file” name=“”/>
*
* 文件上传:
* 步骤1: 文件上传需要定义两个变量 1 文件、 2 文件名
* 步骤2:生成get、set方法
* 步骤3:具体方法中代码:
* 1、在创建上传文件地址(服务器端)
* 2、上传文件
*/
private File upload; //文件 名称与jsp页面中input文件输入项中name值相同
private String uploadFileName; //文件名:文件+FileName
public File getUpload() {
return upload;
}
public void setUpload(File upload) {
this.upload = upload;
}
public String getUploadFileName() {
return uploadFileName;
}
public void setUploadFileName(String uploadFileName) {
this.uploadFileName = uploadFileName;
}
//添加联系人
//需要封装表单数据到action中
public String addLinkMan() throws IOException{
/**
* 可以封装联系人信息,但不能直接封装表单中cid值,因为cid是customer中的属性。
*
* 解决:在页面中传入customer.cid 则可将cid传入linkman中的customer属性中 ,则可完成封装
*/
System.out.println(linkMan);
linkManService.addLinkMan(linkMan);
//上传过程 判断是否有文件
if(upload != null){
File serverFile = new File("E://sshProjecFileUpload"+"/"+uploadFileName); //1、在创建上传文件地址(服务器端)
FileUtils.copyFile(upload, serverFile); //2、上传文件
}
return "addLinkMan";
}
Jsp页面 上传表单要求
六、no session问题
web.xml中过滤器配置
七、知识点
hibernate外键双向维护
(1)在客户和联系人这两端都需要维护外键
(2)解决方式:让其中的一方放弃关系维护(让一那一方放弃)
客户--联系人级联操作
默认效果:直接删除客户,把联系人外键设置null。
1)inverse属性值false :表示不放弃
如果不配置cascade属性值:直接删除客户,把联系人外键设置null。默认效果
2)inverse属性值true:放弃维护。
1)删除客户时,出现异常。
2)修改客户时,联系人级联修改
3)cascade值delete:级联删除
3)inverse属性值true、cascade值delete :级联删除,操作
sql是关系数据库查询语言,面对的数据库;
hql面向对象,hql操作的是持久化类及其属性
八、多对多配置
核心思想:将一个多对多拆分成两个多对一
实体类创建
public class Visit {
private Integer visitid;
private String visitAddress;
private String visitContent;
//在拜访中表示用户
private User user = new User();
//在拜访中表示客户
private Customer customer = new Customer();
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
九、BaseDAO抽取
1 定义BaseDAO接口
/**
* 定义BaseDAO接口
* @author 一万年行不行
*
* @param <T>
*/
public interface BaseDao<T> {
//添加
void add(T t);
//删除
void delete(T t);
//修改
void update(T t);
//根据id查询
T findById(int id);
//查询所有
List<T> findAll();
}
2 BaseDaoImpl接口实现类
/**
* BaseDaoImpl接口实现类
* 继承extends HibernateDaoSupport
* @author 一万年行不行
*
* @param <T>
*/
public class BaseDaoImpl<T> extends HibernateDaoSupport implements BaseDao<T> {
private Class clazzType;
public BaseDaoImpl() {
//1 获取当前运行对象的class
//比如运行customerDao实现类,得到customerDao实现类class
Class clazz = this.getClass();
//2 获取运行类的父类的参数化类型
Type type = clazz.getGenericSuperclass();
//3 转换成子接口ParameterizedType
ParameterizedType ptype = (ParameterizedType) type;
//4 获取实际类型参数
//比如 Map<key,value>
Type[] types = ptype.getActualTypeArguments();
//5 把Type变成class
Class clazzParameter = (Class) types[0];
this.clazzType = clazzParameter;
}
//根据id查询
public T findById(int id) {
return (T) this.getHibernateTemplate().get(clazzType, id);
}
//查询所有
@SuppressWarnings("unchecked")
public List<T> findAll() {
String className = clazzType.getSimpleName(); //从.class对象中得到为类名 ,查询时需要类名,而不是class对象
return (List<T>) this.getHibernateTemplate().find("from "+className);
}
//添加
public void add(T t) {
this.getHibernateTemplate().save(t);
}
//删除
public void delete(T t) {
this.getHibernateTemplate().delete(t);
}
//修改
public void update(T t) {
this.getHibernateTemplate().update(t);
}
}
3 原接口继承BaseDao接口
/**
* 原接口继承BaseDao接口
*
* extends BaseDao<Customer>
* 注释掉原原接口方法
* @author 一万年行不行
*
*/
public interface CustomerDao extends BaseDao<Customer> {
// void add(Customer customer);
//
// List<Customer> findAll();
//
// Customer findById(int cid);
//
// void delete(Customer c);
//
// void update(Customer customer);
}
4 原实现类 实现 BaseDAOImpl
/**
* 原实现类 实现 BaseDAOImpl
* @author 一万年行不行
*
*/
public class CustomerDaoImpl extends BaseDaoImpl<Customer> implements CustomerDao { ... }
十、多条件查询
第一种 写底层hibernate代码实现
第二种 使用hibernate模板里面find方法实现
- 写hql语句实现
@SuppressWarnings("all")
public List<Customer> findMoreCondition(Customer customer) {
//使用hibernate模板里面find方法实现
//拼接hql语句
String hql = "from Customer where 1=1 ";
//创建list集合,如果值不为空,把值设置到list里面
List<Object> p = new ArrayList<Object>();
//判断条件值是否为空,如果不为空拼接hql语句
if(customer.getCustName()!=null && !"".equals(customer.getCustName())) {
//拼接hql
hql += " and custName=?";
//把值设置到list里面
p.add(customer.getCustName());
}
if(customer.getCustLevel()!=null && !"".equals(customer.getCustLevel())) {
hql += " and custLevel=?";
p.add(customer.getCustLevel());
}
if(customer.getCustSource()!=null && !"".equals(customer.getCustSource())) {
hql += " and custSource=?";
p.add(customer.getCustSource());
}
System.out.println("hql: "+hql);
System.out.println("list: "+p);
return (List<Customer>) this.getHibernateTemplate().find(hql, p.toArray());
}
第三种 使用离线对象和hibernate模板里面方法
//使用离线对象方式实现多条件组合查询
@SuppressWarnings("all")
public List<Customer> findMoreCondition(Customer customer) {
//创建离线对象,指定对哪个实体类进行操作
DetachedCriteria criteria = DetachedCriteria.forClass(Customer.class);
//判断条件值是否为空
if(customer.getCustName()!=null && !"".equals(customer.getCustName())) {
//设置对属性,设置值
criteria.add(Restrictions.eq("custName", customer.getCustName()));
}
if(customer.getCustLevel()!=null && !"".equals(customer.getCustLevel())) {
criteria.add(Restrictions.eq("custLevel", customer.getCustLevel()));
}
if(customer.getCustSource()!=null && !"".equals(customer.getCustSource())) {
criteria.add(Restrictions.eq("custSource", customer.getCustSource()));
}
return (List<Customer>) this.getHibernateTemplate().findByCriteria(criteria);
}
十一、数据字典表
十二、分组统计
统计效果演示
sql语句
分组统计代码
// 联系人级别统计
public List countLinkManLevel() {
// 复杂语句,需要使用底层sql来实现
// 1.得到session
Session session = this.getSessionFactory().getCurrentSession();
// 2.通过session创建底层SQLQuery,并传入查询语句
SQLQuery sqlQuery = session.createSQLQuery(
"SELECT l.num,ll.levelName "+
"FROM (SELECT COUNT(*) AS num ,lkmLevel FROM t_linkman GROUP BY lkmLevel) l "+
"inner join t_lkmlevel ll on l.lkmLevel=ll.levelid");
/*
* 此时查询结果为数组形式,不方便前端页面取值,
*
* 可转换为实体类形式,但没有对应实体类 ( sqlQuery.addEntity(类名.class); )
*
* 则,转换为map形式
*/
//3.将查询结果封装为map
sqlQuery.setResultTransformer(Transformers.aliasToBean(HashMap.class));
List list = sqlQuery.list();
return list;
}