一:字典表
字典信息:在项目中可能会使用到,已经存在的一些信息。 例如,客户的级别:普通用户,vip用户... 客户的来源:网络营销,电话营销... 客户所属行业:电子商务,房地产... 客户的性别:男,女 在保存用户的时候,这些信息都是已经存在的,不应该让用户让用户来任意填写, 而是通过下拉列表来让用户选择。 这些已经存在的信息称之为字典信息,将字典信息保存在字典表中。
二:表的设计
客户表和级别表,来源表和所属行业表的关系
客户和级别表,行业表,来源表都属于多对一的关系 为了简化开发,可以将三张字典数据合成一张字典表
字典表中的内容
三:实体之间的设计
customer表中的cust_level,cust_source,cust_industry字段属于外键 对应着字典表basedict中的dict_id主键 在多方(客户实体)中存在一方对象(字典实体)的引用 Customer实体设计
public class Customer implements Serializable{ private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long cust_id; private String cust_name; private String cust_phone; private String cust_mobile; //配置主外键关系 referencedColumnName外键所指向的主键 name:外键名称 @ManyToOne(targetEntity=BaseDict.class) @JoinColumn(name="cust_level",referencedColumnName="dict_id") private BaseDict level; 客户级别 @ManyToOne(targetEntity=BaseDict.class) @JoinColumn(name="cust_source",referencedColumnName="dict_id") private BaseDict source; 客户来源 @ManyToOne(targetEntity=BaseDict.class) @JoinColumn(name="cust_industry",referencedColumnName="dict_id") private BaseDict industry; 客户所属行业
字典实体(BaseDict)
@Entity @Table(name="base_dict") public class BaseDict implements Serializable{ private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long dict_id; 字典表id private String dict_type_code; 类别(该条记录是来源,行业还是级别的标识) private String dict_type_name; 类别的名称(客户来源,客户行业,客户级别) private String dict_item_name; 来源,级别,行业中的具体项(vip,房地产,网络营销) private String dict_item_code; private Integer dict_sort; 排序使用 private Character dict_enable; private String dict_memo;
知识回顾:Hibernate中查询的api
①:oid 通过id查询 get load方法 ②hql:在HQL语句中不可能出现于数据库相关的信息,因为它是面向对象来操作的, 只会出现实体类中的属性或对象如果出现了表名或者表中的字段, 那么肯定是你的表名和类名相同,表中的字段和类中的属性相同 查询的api Query query = session.createQuery(hql语句); query.list()查询所有 query.uniqueResult()返回单一值 基本查询:from 类名 查询该实体类映射的表中所有的记录封装为List
hql查询续: 排序 String sql = "from Customer order by 属性名 desc"; //desc降序 默认为升序 聚合函数 count|sum|max|min|avg select count(cust_id) from Customer count(*)也ok 投影查询 查询部分列 要求实体类必须要有select后面的构造函数 String hql = "select new Customer(c.cust_id,c.cust_name) from Customer c"; ③:qbc查询 session.createCriteria(持久化类的字节码对象) 分页: setFirstResult(int 开启的索引) setMaxResults(int 每页显示的条数) 排序: addOrder(Order.desc|asc(属性名)); 统计: setProjection(Projections.count|sum|min|max|avg(属性名)) setProjection(Projections.rowCount()) 条件: add(Resitrctions.eq|like|gt|lt(属性名称,参数..));
离线查询对象: DetachedCriteria:api和Criteria中完全相同 DetachedCriteria dc = DetachedCriteria(持久化类的字节码对象); 在web层进行使用,在条件查询的时候对查询的条件进行封装,在调用service层 方法得时候,只需要传递dc对象就ok。 HibernateTemplate(Hibernate模板操作数据库) save(obj) update(obj) delete(obj) get(class,id) load(class,id) findByCriteria(DetachedCriteria dc):qbc查询 findByCriteria(DetachedCriteria dc,int firstResult,int maxResults):qbc分页 find(String hql,Object... args):hql
需求一:添加客户到数据库中
添加客户的页面:
在添加用户的时候,要先从数据库中查询出客户级别,信息来源,所属行业 这些字典信息来让用户进行选择 由两种方式: ①:同步方式 先跳转到action,将查询到的字典内容放置到值栈中,然后再请发到add.jsp ②:异步方式 直接到add.jsp页面,当页面加载完成的时候发送ajax请求
先使用第一种方式:
//查询字典数据 public void getDictValue(){ 通过类别可以查询到来源,级别,所属行业下的所有数据 levelList = customerService.findBaseDictByTypeCode("006"); sourceList = customerService.findBaseDictByTypeCode("002"); industryList = customerService.findBaseDictByTypeCode("001"); }
字典数据的集合设置为action的成员属性,提供get方法就可以在jsp页面中获取
add.jsp页面
使用ognl+struts2标签获取致函中的数据(获取客户来源,所属行业类似)客户级别 :
customer表中的cust_level,cust_source,cust_industry字段属于外键 对应着字典表basedict中的dict_id主键 表单中 级别的name属性为:level.dict_id 来源的name属性为: source.dict_id 会使用属性封装的方式封装到BaseDict level中的dict_id 在保存Customer的时候会将对应的外键(cust_level,cust_source,cust_industry)保存
需求2:查询客户列表信息(分页+条件查询)
分页使用的ObjectVlaue(PageBean)部分代码 mysql分页 limit ?,? 后台需要的条件:起始索引,一页显示的记录数 前台需要的数据:当前页显示的数据,总页数,总的记录数 前台需要传递的数据:当前页(没有传递默认为1)一页显示的记录数(没有传递,给出默认值) 起始索引:(当前页-1)*一页显示的记录数 总记录数:从数据库中查询而来 总页数:Math.ceil(1.0*总记录数/总页数) 当前页显示的数据:数据库中查询
public class PageBeanimplements Serializable { private static final long serialVersionUID = 1L; private Integer startIndex = 0; //起始索引 private Integer pageSize = 3; //一页显示的记录数 private Integer pageNumber = 1; //当前页,由前端用户传递,如果用户没有传递默认显示第一页的数据 private List result; //封装查询出来的某一页的数据 private Long totalRecord; //总记录数 从数据库中查询而来 //计算而来 总记录数%一页显示的记录==0?总记录数/一页显示的记录:总记录数/一页显示的记录+1 private Integer totalPage; //总页数 public Integer getTotalPage() { totalPage = (int) Math.ceil(1.0*totalRecord/pageSize); return totalPage; } //外界来获取起始索引 在内部进行计算 public Integer getStartIndex() { startIndex = (pageNumber - 1) * pageSize; return startIndex; } public void setPageSize(Integer pageSize) { if(pageSize != null){ 如果前台没有传递,使用默认值 this.pageSize = pageSize; }else{ this.pageSize = 3; } } public void setPageNumber(Integer pageNumber) { if(pageNumber != null){ this.pageNumber = pageNumber; }else{ 如果前台没有传递,使用默认值 this.pageNumber = 1; } } }
条件查询:在web层使用DetachedCriteria来封装查询的条件 web层action中的代码 使用DetachedCriteria来封装查询的条件,使用pageBean来封装当前页和一页显示的数据 调用service层方法的时候传递DetachedCriteria对象和pageBean对象
DetachedCriteria dc = DetachedCriteria.forClass(Customer.class); /* * dc.add(Restrictions(propertyName, value))查询提交 propertyName:是属性字段 value是条件对应的值 * 然后再使用对象导航查询去字典表中查询数据 */ if(customer.getCust_name() != null &&!customer.getCust_name().trim().equals("")){ dc.add(Restrictions.like("cust_name","%"+customer.getCust_name().trim()+"%")); } /* * 对查询的条件进行判断和封装 不需要对对象进行非空判断, 因为使用模型封装实体的时候,实体必须手动创建 * 如果下拉列表没有没有被选择,select传递的value为-1 */ if(customer.getLevel() != null && customer.getLevel().getDict_id() != -1){ dc.add(Restrictions.eq("level", customer.getLevel())); } if(customer.getSource() != null && customer.getSource().getDict_id() != -1){ dc.add(Restrictions.eq("source", customer.getSource())); } if(customer.getIndustry() != null && customer.getIndustry().getDict_id() != -1){ dc.add(Restrictions.eq("industry", customer.getIndustry())); } //传递的参数为离线查询对象(封装条件)和分页需要的pageBean pageBean = customerService.findList(dc,pageBean);
service层的代码
public PageBean findList(DetachedCriteria dc,PageBean pageBean) { /* * 需要填充的数据为当前页显示的数据 * 总的记录数 */ //查询总的记录数(需要传递dc离线查询对象,因为不止是分页,是条件+分页) Long totalRecord = customerDao.searchTotalRecord(dc); pageBean.setTotalRecord(totalRecord); //查询当前页显示的记录 Listresult = customerDao.searchCustomerList(dc,pageBean); pageBean.setResult(result); return pageBean; }
dao层的代码:
@Override public Long searchTotalRecord(DetachedCriteria dc) { //设置投影(聚合) dc.setProjection(Projections.rowCount()); /* * 查询语句类似于 select count(*) from Customer * 如果有条件的分页就是select count(*) from Customer where ...... */ Listrecord = (List ) hibernateTemplate.findByCriteria(dc); /* * 查询完成之后需要去除投影,因为查询完成总记录数之后 * 还需要查询当前页显示的数据 使用的是一个离线查询对象 */ dc.setProjection(null); return record.get(0); } //分页+条件查询 查询当前页显示的数据 @Override public List searchCustomerList(DetachedCriteria dc, PageBean pageBean) { List customerList = (List ) hibernateTemplate.findByCriteria(dc, pageBean.getStartIndex(), pageBean.getPageSize()); return customerList; }
要注意的就是在进行总记录数查询的时候dc.setProjection(Projections.rowCount()); 当查询完成的时候去去除投影,因为接下来查询当前页显示的记录数使用的也是同一个 离线查询对象。
查询条件的回显
两种方式:
方式一:使用el表达式进行判断
方式二:使用jqery属性选择器 使用jquery的属性选择器来进行判断 当下拉列表中option中的值与之前选择的值相同时,让匹配的option选中
前台页面: 当点击下一页或者索引页的时候,使用的是超链接。 但是分页+条件查询,查询的条件在表单中 解决:当点击索引页。上一页下一页之后。 为超链接提供点击事件: 将当前点击的页数动态的放置在表单输入项中 然后提交表单,这样会将查询的条件和当前页一起提交到后台页面
当前页的表单 后一页 function toPage(pageNumber){ //获取到提交当前页的输入框 $("#pageNumberId").val(pageNumber); //提交表单 $("#customerForm").submit(); }
需求三:修改客户信息: 先查询后修改: 据客户的id先查询客户信息,然后请求转发到edit.jsp页面显示要修改的客户信息 private Customer customer = new Customer(); //使用模型驱动进行表单数据的封装 @Override public Customer getModel() { return customer; } 使用模型驱动封装要修改的的客户id 此时customer中只有客户的id 这时候如果还使用customer来接收的话,请求转发到edit.jsp页面中 通过${成员属性}是获取不到内容的 因为customer引用重新指向了一个对象 要想在edit.jsp页面中获取到查询到的内容 有三种方式: 一:使用customer来接收 使用ActionContext.getContext.getValueStack().push(customer); 向root栈中存放customer指向的新对象 可以使用${成员属性获取到新内容} 二:创建一个新的Customer findCustomer对象来接收,为该customer也提供相应的get方法 在页面就可以使用${findCustomer.成员属性}来获取导内容 三:使用模型封装的customer来接收 在edit.jsp页面就可以使用${model.成员属性}的方法来获取到内容
no-session问题: could not initialize proxy - no Session 原因:懒加载的问题 使用id获取要修改的对象时,使用load方法 访问 service 访问dao 返回是linkman的代理对象 代理对象返回给web层 页面获取数据 代理对象在使用时才会真正的去查询 但是session已经关闭
no-session的解决方案:
1、立即查询 2、延长session的存活时间 在web层将代理对象的数据查询完毕后在让session关闭 spring提供了一个Filter ----- OpenSessionInViewOpenSessionInViewFilter org.springframework.orm.hibernate5.support.OpenSessionInViewFilter 注意一:必须让该过滤器在Struts2核心过滤器之前执行 因为当Struts核心过滤器执行完成之后,action的方法已经被执行 在操作数据库的时候session对象还没有创建,还是会有no-session问题 注意二:OpenSessionInViewFilter hibernate5.support.OpenSessionInViewFilter 必须和你导入Hibernate的版本一致,在这里是Hibernate5版本 否则可能会有异常: org.hibernate.SessionFactory.openSession()Lorg/hibernate/classic/Session OpenSessionInViewFilter /*
no-session:懒加载问题
原生hibernate(非jpa形式) 类级别的延迟 load() 直接查询实体对象时是否延迟 配置默认true 关联级别的延迟 关联类的延迟 linkman关联customer ----默认proxy 关联集合的延迟 customer关联linkman ---默认是true
jpa形式的hibernate 一对多 查询一的一方多 多的一方默认延迟加载 查询customer 对应的linkman 延迟加载 多对一 查询多的一方 一的一方默认立即加载 查询linkman 对应的customer的立即加载
删除客户(一方)删除客户的时候要删除客户下对应的联系人,配置级联删除 cascade=CascadeType.REMOVE 一方放弃维护关系,所以不能直接删除,需要先查后删 //在删除一方的时候先查询后删除,因为查询出来的对象和多表的一方存在关系,可以级联删除 customer = hibernateTemplate.get(Customer.class, customer.getCust_id()); hibernateTemplate.delete(customer); 删除多方的时候可以直接删除,单表操作的时候可以直接删除
ajax的递归错误 There is a cycle in the hierarchy 在json格式转换的时候出现死循环 表之间存在关系,Customer和LinkMan表是一对多的关系 在打印customer对象的时候,由于customer中存在LinkMan 会打印LinkMan对象,在打印LinkMan对象中,又存在Customer对象 递归调用,导致内存溢出 需求: 添加联系人的时候要选择所属客户 在add.jsp页面加载完成的时候,发送ajax请求 获取数据库中的所有客户信息
web层的代码:
//去数据库中查找所有的客户信息 @Action("customer_findCustomerList") public void findCustomerList() throws IOException{ ListcustomerList = customerService.findAll(); //发送的是ajax请求 转换为json格式的数据 JsonConfig jsonConfig = new JsonConfig(); 设置不参与转换的字段 jsonConfig.setExcludes(new String[]{"linkMen"}); String json = JSONArray.fromObject(customerList,jsonConfig).toString(); //将json格式的数据写会给浏览器,解决中文乱码问题 HttpServletResponse response = ServletActionContext.getResponse(); response.setCharacterEncoding("UTF-8"); response.getWriter().println(json); }
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/67588.html
一:字典表 字典信息:在项目中可能会使用到,已经存在的一些信息。 例如,客户的级别:普通用户,vip用户... 客户的来源:网络营销,电话营销... 客户所属行业:电子商务,房地产... 客户的性别:男,女 在保存用户的时候,这些信息都是已经存在的,不应该让用户让用户来任意填写, 而是通过下拉列表来让用户选择。 这些已经存在的信息称之为字典信...
摘要:前言由于写的文章已经是有点多了,为了自己和大家的检索方便,于是我就做了这么一个博客导航。 前言 由于写的文章已经是有点多了,为了自己和大家的检索方便,于是我就做了这么一个博客导航。 由于更新比较频繁,因此隔一段时间才会更新目录导航哦~想要获取最新原创的技术文章欢迎关注我的公众号:Java3y Java3y文章目录导航 Java基础 泛型就这么简单 注解就这么简单 Druid数据库连接池...
摘要:需求整合框架做一个保存用户的业务,业务比较简单,重在框架整合。 需求:整合ssh框架做一个保存用户的业务,业务比较简单,重在ssh框架整合。创建数据库和表 CREATE DATABASE ssh01; USE DATABASE; 表由Hibernate创建,可以看配置是否成功 一:导入jar包 Hibernate需要jar Hibernate基本jar mysql驱动 ...
摘要:在结构上引入了头结点和尾节点,他们分别指向队列的头和尾,尝试获取锁入队服务教程在它提出十多年后的今天,已经成为最重要的应用技术之一。随着编程经验的日积月累,越来越感觉到了解虚拟机相关要领的重要性。 JVM 源码分析之 Jstat 工具原理完全解读 http://click.aliyun.com/m/8315/ JVM 源码分析之 Jstat 工具原理完全解读 http:...
阅读 884·2021-10-18 13:32
阅读 3429·2021-09-30 09:47
阅读 2116·2021-09-23 11:21
阅读 1843·2021-09-09 09:34
阅读 3438·2019-08-30 15:43
阅读 1486·2019-08-30 11:07
阅读 1019·2019-08-29 16:14
阅读 649·2019-08-29 11:06