关于spring中对延迟关闭session的配置,以及工具类BaseDao和BaseAction
一、HibernateTemplate执行查询时的一些小问题
1.当两个PO类的关系是多对一时:
我们知道在多对一关系中,查询总是会先查询“一”的一方,而一的一方中是含有集合的,一谈到集合就不得不考虑关于hibernate中集合的加载策略,
集合的加载策略默认是懒加载的,我们可以自己手动的去配guanlu置这个加载策略!
然而我们这里讨论的是:当查询“一”的一方的数据完毕时session就会被关闭,而集合中的数据并没有加载所以在页面想获取时是获取不到的,我们想要懒加载能加载到数据,所以必须要延迟关闭session这样才能保证能查询到数据!
而这里我们想要延迟关闭session应该怎么做呢?spring提供了一个过滤器OpenSessionInViewFilter,只要请求还没有结束就不会关闭session
在web.xml中配置这样一个过滤器
<!-- 延迟关闭session的过滤器 ,这个过滤器是由spring提供的 需要在struts过滤器之前执行才可以这样一来session就是由我们这个过滤器创建的,到时候关闭也是由过滤器延迟关闭-->
<filter>
<filter-name>openSession</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>openSession</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
二、关于工具类BaseDao的思想与编写!
1.根据项目代码可知,我们在Dao层操作的代码很多都是重复性很大的代码,而人对于重复性的东西是能简化就简化的,例如增删改查基本上在每个Dao中的相似性极高,我们就可以将重复性的代码抽取出来,将其写成一个工具类,然后以后的Dao只需要去继承这个类即可,不需要再去写一些重复性的代码!
2.思想的构图如下:
在这里我们要附带介绍一下this关键字
this是指当前运行时类的对象,不是指当前对象,注意 观察下面代码的结果
public class Parent {
public void init(){
System.out.println("我是Parent的init 1");
this.demo();
}
public void demo(){
System.out.println("我是Parent 的 demo 2");
}
}
public class Child extends Parent {
@Test
public void fun(){
init();
}
//研究发现this关键字,是指的运行时类的对象
public void init(){
super.init();
System.out.println("我是Child的 init 3");
this.demo();
}
public void demo(){
System.out.println("我是Child的demo 4");
}
}
运行后的结果为:
我是Child的 init 1
我是Child的 init 4
我是Child的 init 3
我是Child的 init 4
所以this指的是当前运行时类的对象
3.如图我们所考虑的是泛型里面将会是什么类型,我们如何得到这个类型来编写HQL语句?这个是我们所要考虑的!
解决办法:① 我们不知道泛型将会是什么类型可以将BaseDao定义为泛型类用变量T来表示泛型的类型 ②我们在BaseDao的实现类中BaseDaoImpl里面的无参构造里面加上这样的代码,当Spring的IOC创建Dao层的实例时这个构造就会被调用:(下面是代码片段)
//用来表示当前运行时类的泛型的类型 private Class<?> beanClass; //TODO BaseDaoImpl的编写 public BaseDaoImpl() { //得到被参数化的类型实例 ParameterizedType parameterizedType=(ParameterizedType) this.getClass().getGenericSuperclass();//得到运行时类的泛型类型 //得到泛型的实际类型,而且这里的泛型只有一个。 beanClass =(Class<?>) parameterizedType.getActualTypeArguments()[0]; }
ParameterizedType这个类用来得到被参数化的类型的实例,然后调用该类的getActualTypeArguments()得到Type[]类型的数组,因为是由泛型的类型转换过来的所以我们可以放心强转为Class类型
Class类型是Type类型的子类
然后这个工具类的编写步骤和代码如下:
①创建一个接口为BaseDao(面向接口编程),在里面定义好所有常用的抽象方法,如,查询所有,根据OID查询,条件查询,条件查询的分页版,根据OID删除,添加方法、修改方法等
②创建实现类BaseDaoImpl,实现类只用去实现接口的所有方法即可。由上图可知我们每一个Dao都需要继承HibernateDaoSupport,所以我们继承这个类,到时候其他Dao只需要继承本实现类即可
③编写正常的Dao接口,这个接口为了得到BaseDao接口里面的方法和最后调用BaseDaoImpl所以必须继承这个接口得到里面所有的抽象方法
④编写正常Dao接口的实现类,由接口中已经继承了HibernateDaoSupport类所以我们只需要继承这个类即可使用所有的方法,这样使代码得到重用!
BaseDao<T>接口代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public interface BaseDao<T> { //保存用户 public void save(T t); //删除指定用户 public void delete(T t); //保存或更新 public void saveOrUpdate(T t); //查询所有 public List<T> findAll(); //根据Id查询指定数据 public T findById(Serializable id); //单纯的条件查询 public List<T> findAll(String condition, Object[] params); //带分页的条件查询 public int getTotalRecord(String condition,Object[] params); public List<T> findAll(String condition, Object[] params, int startIndex, int pageSize); //离线条件查询查询,可以可以参考hibernate笔记。 public List<T> findAll(DetachedCriteria criteria); //离线查询的分页版本 public List<T> findAll(DetachedCriteria criteria, int startIndex,int pageSize); }
BaseDaoImpl<T>实现类代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class BaseDaoImpl<T>extends HibernateDaoSupport implements BaseDao<T> { //用来表示当前运行时类的泛型的类型 private Class<?> beanClass; //TODO BaseDaoImpl的编写 public BaseDaoImpl() { //得到被参数化的类型实例 ParameterizedType parameterizedType=(ParameterizedType) this.getClass().getGenericSuperclass();//得到运行时类的泛型类型 //得到泛型的实际类型,而且这里的泛型只有一个。 beanClass =(Class<?>) parameterizedType.getActualTypeArguments()[0]; } //保存用户 public void save(T t) { this.getHibernateTemplate().save(t); } //删除指定用户 public void delete(T t) { this.getHibernateTemplate().delete(t); } //保存或更新,如果含有OID标识则会进行更新,如果没有OID标识那么就进行保存 public void saveOrUpdate(T t) { this.getHibernateTemplate().saveOrUpdate(t); } public T findById(Serializable id) { // TODO Auto-generated method stub return (T) this.getHibernateTemplate().get(beanClass,id); } //查询所有初级版 public List<T> findAll() { //getName()得到的是全限定类名,在hql里面指定全限定类名才是最完整的写法 return this.getHibernateTemplate().find("from "+beanClass.getName()); } //查询所有中级版之条件查询 public List<T> findAll(String condition, Object[] params) { String hql="from "+beanClass.getName()+" where 1=1"+condition; // TODO Auto-generated method stub return this.getHibernateTemplate().find(hql, params); } //关于分页的方法 public int getTotalRecord(String condition,Object[] params) { //通过hql语句的条件查询的之聚合函数查询 List<Long> count =this.getHibernateTemplate().find("select count(c) from "+beanClass.getName()+" c where 1=1"+condition,params); return count.get(0).intValue(); } ////查询所有终级版之条件查询 public List<T> findAll(String condition, Object[] params, int startIndex, int pageSize) { String hql="from "+beanClass.getName()+" where 1=1 " +condition; //也可以直接使用QBC查询方式里面提供了分页的API但是HQL查询没有实现分页API我们需要自己去完善 //执行execute()方法需要在里面传递一个回调对象,我们可以直接去实现接口实现回调接口 List<T> list=this.getHibernateTemplate().execute(new MyHibernateCallback<T>().setHql(hql).setParams(params).setStartIndex(startIndex).setPageSize(pageSize)); return list; } //离线条件查询 public List<T> findAll(DetachedCriteria criteria) { return this.getHibernateTemplate().findByCriteria(criteria); } //离线条件查询的分页版 public List<T> findAll(DetachedCriteria criteria, int startIndex, int pageSize) { // TODO Auto-generated method stub return this.getHibernateTemplate().findByCriteria(criteria, startIndex, pageSize); } }
StaffDao接口如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public interface StaffDao extends BaseDao<CrmStaff> { public CrmStaff find(String loginName,String loginPwd); /* 继承工具接口BaseDao的到接口中所有的抽象方法 * public List<CrmStaff> findAll(); public CrmStaff findById(String staffId);*/ /*public CrmStaff updateStaff(String staffId);*/ }
StaffDaoImpl实现如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class StaffDaoImpl extends BaseDaoImpl<CrmStaff> implements StaffDao { //这个longin是我们保留下来的独有的方法,其他方法在BaseDaoImpl中已经写好了 public CrmStaff find(String loginName, String loginPwd) { //HibernateTemplate和Session一样所以可以使用HQL语句查询 List<CrmStaff> list=this.getHibernateTemplate().find("from CrmStaff where loginName=? and loginPwd=?", loginName,loginPwd); if(list.size()==1){ return list.get(0); } return null; } /* public CrmStaff updateStaff(String staffId) { // TODO Auto-generated method stub return this.getHibernateTemplate().get(CrmStaff.class, staffId); }*/ /*我们已经写好了工具类只需要继承就可以实现重复的Dao层代码 * public List<CrmStaff> findAll() { return this.getHibernateTemplate().find("from CrmStaff"); } //编辑前的查询操作,通过Id查询,还有修改时先根据Id查询到持久态的对象再进行修改 public CrmStaff findById(String staffId){ //使用get方法得到对象 return this.getHibernateTemplate().get(CrmStaff.class, staffId); }*/ }
三、BaseAction工具类的编写细节
1.在动作类中有许多重复的东西,例如:需要依赖Service层、需要继承ActionSupport类、需要实现模型驱动,还有常用的方法 push、set、put、session域的获得等都是重复的需要写的东西,我们可以写一个工具类来简化这些代码!
2.编写的思想和BaseDao一样,所以思考的地方还是泛型如何获得,如何编写如何创建模型驱动所需要的实例,我们比BaseDao多需要一个东西,那就是反射使用newInstance来创建模型驱动需要的实例。代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class BaseAction<T> extends ActionSupport implements ModelDriven<T> { private T t; public T getModel() { return this.t; } public BaseAction() { try { //(得到运行时类的泛型)得到被参数化的类型 ParameterizedType parameterizedType=(ParameterizedType) this.getClass().getGenericSuperclass(); Class<?>clazz=(Class<?>) parameterizedType.getActualTypeArguments()[0]; this.t=(T) clazz.newInstance(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } /* * 常用的一些方法的简写: * 压栈方法push * set方法 * put方法 * 放到会话Session中 * 放到Application中 */ public void push(Object o){ ActionContext.getContext().getValueStack().push(o); } //将对象包装成Map放到栈顶,如果栈顶就是一个Map那么直接将数据放进去,取的key就能取到对象 public void set(String key,Object o){ ActionContext.getContext().getValueStack().set(key, o); } //将对象放到ActionContext中 public void put(String key,Object value){ ActionContext.getContext().put(key, value); } //将对象信息放到session域中(放到session域中的方式有两种,一个是直接put进值栈,一个是得到HttpSession对象调用setAttribute放入进去) //这个是直接put进去的 public void putSession(String key,Object value){ /*HttpSession session=ServletActionContext.getRequest().getSession(); session.setAttribute(key, value);*/ ActionContext.getContext().getSession().put(key, value); } //将对象信息放到application域中(放到application域中的方式有两种,一个是直接put进值栈,一个是得到ServletContext对象调用setAttribute放入进去) public void putApplication(String key,Object value){ /*ServletContext application = ServletActionContext.getServletContext(); application.setAttribute(key, value);*/ ActionContext.getContext().getApplication().put(key, value); } /***********************上面都是公共的东西,下面由spring注入实例来使用*********************************/ //spring注入service是默认为单例的,只要不是修改的成员变量则不会出现并发访问问题,这里我们调用的全是方法所以不会出现并发访问问题 //员工的service private StaffService staffService; //如果你导入了插件包以后你这个setter方法的名称与spring是id名称对应时就会自动注入 public void setStaffService(StaffService staffService) { this.staffService = staffService; } //职位的service private PostService postService; public void setPostService(PostService postService) { this.postService = postService; } //部门的service private DepService depService; public void setDepService(DepService depService) { this.depService = depService; } //课程类别的service private CourseTypeService courseTypeService; public void setCourseTypeService(CourseTypeService courseTypeService) { this.courseTypeService = courseTypeService; } //班级的service private ClassesService classesService; public void setClassesService(ClassesService classesService) { this.classesService = classesService; } //注意当进程这个工具类时为了让子类得到由spring创建的service实例(1.安装了整合插件2.名字和spring service类 id名称相同。那么就会自动注入) //我们需要提供get方法 public StaffService getStaffService() { return staffService; } public PostService getPostService() { return postService; } public DepService getDepService() { return depService; } public CourseTypeService getCourseTypeService() { return courseTypeService; } public ClassesService getClassesService() { return classesService; } }
然后想要编写Action类时就只需要继承BaseAction即可,所有的东西已经写进去,而且当服务器接收一个请求,就会创建一个Action的实例所以BaseAction的构造方法也会被初始化进去!
继承BaseAction代码如下:可以看到我们将从前复杂的代码进行了简化,而且编写通用的模型驱动,每个Action所单独的Service
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//继承BaseAction进行改造 public class StaffAction extends BaseAction<CrmStaff> { /*private StaffService staffService; //如果你导入了插件包以后你这个setter方法的名称与spring是id名称对应时就会自动注入 public void setStaffService(StaffService staffService) { this.staffService = staffService; }*/ //使用模型驱动必须自己创建对象容器然后等着表单传递过来然后封装到模型驱动中,前提是你要继承ActionSupport /*private CrmStaff crmStaff = new CrmStaff(); public CrmStaff getModel() { return crmStaff; }*/ /***********登录验证*************/ public String login() throws Exception { // 登录验证 //CrmStaff sqlCrmStaff=staffService.login(crmStaff); //改造后 CrmStaff sqlCrmStaff=getStaffService().login(this.getModel()); //如果返回值不为null则保存session if(sqlCrmStaff!=null){ /*HttpSession session=ServletActionContext.getRequest().getSession(); session.setAttribute("sqlsqlCrmStaff", sqlCrmStaff);*/ //改造后,装进session域中 putSession("sqlsqlCrmStaff", sqlCrmStaff); return SUCCESS; } //如果为null则保存错误信息,this.addFieldError("", "*用户名或密码错误!");没有指定错误字段的位置 this.addFieldError("", "用户名或密码错误!"); //请求转发显示到登录页面 return "login"; } public String home(){ return "home"; } /***********查询所有员工*************/ public String findAll() throws Exception { // 1.查询所有员工 /*List<CrmStaff> crmStaffs=staffService.findAllStaff();*/ //改造后 List<CrmStaff> crmStaffs=getStaffService().findAllStaff(); /****************************************************************/ // 2 将结果存放到值栈,方便jsp获得数据 // * 方式1:context (map)存放 put(key ,value) ,jsp页面获得 “#key” 。 // ActionContext.getContext().put(key, value) // * 方式2:root (值栈) ,push(obj) ,一般数据为JavaBean 或 Map ,jsp页面获得“属性名” 或“key” 。 // ActionContext.getContext().getValueStack().push(o) // * 方式3:root (值栈) ,set(key ,value) ,一般数据为List ,jsp页面获得“key” // set() 底层 new Map(key,value) ,将 push(map ) /****************************************************************/ /* ActionContext.getContext().put("crmStaffs", crmStaffs); */ //改造后 put("crmStaffs", crmStaffs); return "findAll"; } /***********编辑前的回显工作*************/ //已经在工具BaseAction中定义,当我们继承时方法已经到这个类里面来了,只是我们看不到 /*private DepService depService; public void setDepService(DepService depService) { this.depService = depService; }*/ public String findPreEdit(){ //1.得到请求传来的参数 /*CrmStaff oneStaff= staffService.findPreEidt(crmStaff.getStaffId());*/ //改造后 CrmStaff oneStaff= getStaffService().findPreEidt(this.getModel().getStaffId()); //2.将得到的对象压入栈顶 ActionContext.getContext().getValueStack().push(oneStaff); //改造后 push(oneStaff); //3.得到所有的部门然后ValueStack的set方法set进值栈中 //查询所有部门有问题,需要解决 /*List<CrmDepartment> allDep = depService.findAllDep();*/ //改造后 List<CrmDepartment> allDep = getDepService().findAllDep(); /*ActionContext.getContext().getValueStack().set("allDep",allDep);*/ //改造后 set("allDep",allDep); return "findPreEdit"; } /**********保存编辑内容****************/ public String edit(){ //保存所有的传过来的信息 /*staffService.updateStaff(crmStaff);*/ //改造后 getStaffService().updateStaff(this.getModel()); return "edit"; } }
可以多看看细细品味!!!