OpenSessionInview:
1.如果当前方法没有事物环境,则调用完毕getHibernate以后。session关闭;
说明:1如果测试dao层,没有事物环境
2如果测试service层,但是如果service层的方法没有包含在事物的声明中,则也没有事物环境
2.如果当前方法有事物环境,则不能关闭session
3.如果事物环境方法被调用完毕以后,session关闭
背景:
hibernate 允许对关联对象、属性进行延迟加载,但是必须保证延迟加载的操作限于同一个 Hibernate Session 范围之内进行。如果 Service 层返回一个启用了延迟加载功能的领域对象给 Web 层,当 Web 层访问到那些需要延迟加载的数据时,由于加载领域对象的 Hibernate Session 已经关闭,这些导致延迟加载数据的访问异常。
把一个Hibernate Session和一次完整的请求过程对应的线程相绑定。目的是为了实现”Open Session in View”的模式。例如: 它允许在事务提交之后延迟加载显示所需要的对象。
OpenSessionInViewFilter 过滤器将 Hibernate Session 绑定到请求线程中,它将自动被 spring 的事务管理器探测到。所以 OpenSessionInViewFilter 适用于 Service 层使用HibernateTransactionManager 或 JtaTransactionManager 进行事务管理的环境,也可以用于非事务只读的数据操作中。
在不同的技术框架下,实现Open session in view的手段不同:
在servlet中用过滤器实现
在struts中用拦截器实现
在spring中使用AOP实现
这里给出在ssh框架中用spring的AOP实现:
配置:
1 <filter> 2 <filter-name>Spring OpenSessionInViewFilter</filter-name> 3 <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class> 4 <init-param> 5 6 <!-- 7 指定org.springframework.orm.hibernate3.LocalSessionFactoryBean在spring配置文件中的名称,默认值为sessionFactory 8 如果LocalSessionFactoryBean在spring中的名称不是sessionFactory,该参数一定要指定,否则会出现找不到sessionFactory的异常 9 --> 10 <param-name>sessionFactoryBean</param-name> 11 <param-value>sessionFactory</param-value> 12 </init-param> 13 </filter> 14 <filter-mapping> 15 <filter-name>Spring OpenSessionInViewFilter</filter-name> 16 <url-pattern>/*</url-pattern> 17 </filter-mapping>
原理:
1 protected void doFilterInternal( 2 HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) 3 throws ServletException, IOException { 4 SessionFactory sessionFactory = lookupSessionFactory(request); 5 boolean participate = false; 6 if (isSingleSession()) { 7 // single session mode 8 if (TransactionSynchronizationManager.hasResource(sessionFactory)) { 9 // Do not modify the Session: just set the participate flag. 10 participate = true; 11 } 12 else { 13 logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter"); 14 Session session = getSession(sessionFactory); 15 TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session)); 16 } 17 } 18 else { 19 // deferred close mode 20 if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) { 21 // Do not modify deferred close: just set the participate flag. 22 participate = true; 23 } 24 else { 25 SessionFactoryUtils.initDeferredClose(sessionFactory); 26 } 27 } 28 try { 29 filterChain.doFilter(request, response); 30 } 31 finally { 32 if (!participate) { 33 if (isSingleSession()) { 34 // single session mode 35 SessionHolder sessionHolder = 36 (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory); 37 logger.debug("Closing single Hibernate Session in OpenSessionInViewFilter"); 38 closeSession(sessionHolder.getSession(), sessionFactory); 39 } 40 else { 41 // deferred close mode 42 SessionFactoryUtils.processDeferredClose(sessionFactory); 43 } 44 } 45 } 46 } 47 48 49 protected void doFilterInternal(HttpServletRequest request, 50 HttpServletResponse response, FilterChain filterChain) 51 throws ServletException, IOException { 52 /** 53 * 从spring的上下文中取得SessionFactory对象 54 * WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()); 55 * return (SessionFactory) wac.getBean(getSessionFactoryBeanName(),SessionFactory.class); 56 * getSessionFactoryBeanName()方法默认返回"sessionFactory"字符串,在spring配置文件中可要注意了,别写错了. 57 */ 58 SessionFactory sessionFactory = lookupSessionFactory(request); 59 boolean participate = false;// 标识过滤器结束时是否进行关闭session等后续处理 60 if (isSingleSession()) {//单session模式 61 //判断能否在当前线程中取得sessionFactory对象对应的session 62 if (TransactionSynchronizationManager.hasResource(sessionFactory)) { 63 //当能够找到session的时候证明会有相关类处理session的收尾工作,这个过滤器不能进行关闭session操作,否则会出现重复关闭的情况. 64 participate = true;//但我并没有想出正常使用的情况下什么时候会出现这种情况. 65 } else { 66 Session session = getSession(sessionFactory);//当前线程取不到session的时候通过sessionFactory创建,下面还会详细介绍此方法 67 //将session绑定到当前的线程中,SessionHolder是session的一层封装,里面有个存放session的map,而且是线程同步的Collections.synchronizedMap(new HashMap(1)); 68 //但是单session模式下一个SessionHolder对应一个session,核心方法是getValidatedSession 取得一个open状态下的session,并且取出后从map中移出. 69 TransactionSynchronizationManager.bindResource(sessionFactory, 70 71 new SessionHolder(session)); 72 } 73 } else {//这段是非单session模式的处理情况,没有研究.但粗略看上去,大概思想是一样的 74 if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) { 75 participate = true; 76 } else { 77 SessionFactoryUtils.initDeferredClose(sessionFactory); 78 } 79 } 80 try { 81 //将session绑定到了当前线程后,就该处理请求了 82 filterChain.doFilter(request, response); 83 }finally { 84 if (!participate) { 85 if (isSingleSession()) { 86 //当请求结束时,对于单session模式,这时候要取消session的绑定,因为web容器(Tomcat等)的线程是采用线程池机制的,线程使用过后并不会销毁. 87 SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager 88 .unbindResource(sessionFactory); 89 //取消绑定只是取消session对象和线程对象之间的引用,还没有关闭session,不关闭session相对于不关闭数据库连接,所以这里要关闭session 90 closeSession(sessionHolder.getSession(), sessionFactory); 91 } else { 92 //非单session模式,没有研究. 93 SessionFactoryUtils.processDeferredClose(sessionFactory); 94 } 95 } 96 }
步骤:
1. 获取session,打开session
2. filterChain.doFilter(request, response);
3. 关闭session
在2中可能执行了若干的Servlet、JSP、Action等等,最终处理完渲染完页面之后,再进入OpenSessionInViewFilter的3关闭session
现在只要保证在2中不论是Servlet、JSP还是Action中执行DAO时获取的session都是1中打开的同一个session,并且在DAO关闭session时并不实际关闭,留到OpenSessionInViewFilter的3中再最终关闭,就实现了懒加载了,因为只要是在OpenSessionInViewFilter过滤的范围内,session都处于打开,比如在一个Servlet中查到一个Bean,这时他的关联实体并没有加载,当这个Servlet重定向到一个JSP,在其中得到这个Bean后直接访问之前没加载的那些关联实体,会实时的加载这个关联实体,因为session还未关闭,这便是懒加载了。
解决方法:使用spring提供的过滤器
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> 3 <display-name>day04-02-itheima08-s2sh</display-name> 4 <listener> 5 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 6 </listener> 7 <context-param> 8 <param-name>contextConfigLocation</param-name> 9 <param-value>classpath:spring/applicationContext.xml</param-value> 10 </context-param> 11 12 <filter> 13 <filter-name>OpenSessionInViewFilter</filter-name> 14 <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class> 15 </filter> 16 <filter-mapping> 17 <filter-name>OpenSessionInViewFilter</filter-name> 18 <url-pattern>*.action</url-pattern> 19 </filter-mapping> 20 21 22 <filter> 23 <filter-name>struts2</filter-name> 24 <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> 25 </filter> 26 <filter-mapping> 27 <filter-name>struts2</filter-name> 28 <url-pattern>/*</url-pattern> 29 </filter-mapping> 30 31 32 <welcome-file-list> 33 <welcome-file>index.jsp</welcome-file> 34 </welcome-file-list> 35 </web-app>