开发者社区> 风月无边> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

用 OpenSessionInViewInterceptor 的思路解决 Spring框架中的Hibernate Lazy

简介: 众所周知, 为了解决 Hibernate Lazy 问题, Spring 中引入了 OpenSessionInViewInterceptor, 这样虽然解决了页面上的 Lazy Load 问题,却增加了各层之间的偶合性, 如果一个 Lazy 的 Collection 在页面上可以被正确的 load, 但是如果请求不是来自于 HttpServletRequest (比如在 TestCase 或
+关注继续查看

众所周知, 为了解决 Hibernate Lazy 问题, Spring 中引入了 OpenSessionInViewInterceptor, 这样虽然解决了页面上的 Lazy Load 问题,却增加了各层之间的偶合性,
如果一个 Lazy 的 Collection 在页面上可以被正确的 load, 但是如果请求不是来自于 HttpServletRequest (比如在 TestCase 或 Service 中希望获取 lazy 的属性),
一般会导致两种错误:

代码
  1. 1. 设置了 lazy = "true"  
  2.    会导致 org.hibernate.LazyInitializationException: failed to lazily initialize a collection of xxx: xxx - no session or session was closed   
  3. 2. 设置里 lazy = "false"  
  4.    会导致 org.hibernate.LazyInitializationException: could not initialize proxy - the owning Session was closed   

 

为了方便测试, 灵活使用 lazy load, 我按照 OpenSessionInViewInterceptor 的思路实现了一个 HibernateLazyResolber, 代码如下:

 

代码
  1.   
  2. /*   
  3.  * Copyright 2004-2005 wangz.   
  4.  * Project shufe_newsroom   
  5.  */   
  6. package org.summerfragrance.support.hibernate3;   
  7.   
  8. import org.apache.commons.logging.Log;   
  9. import org.apache.commons.logging.LogFactory;   
  10. import org.hibernate.FlushMode;   
  11. import org.hibernate.Session;   
  12. import org.hibernate.SessionFactory;   
  13. import org.springframework.beans.factory.InitializingBean;   
  14. import org.springframework.dao.DataAccessResourceFailureException;   
  15. import org.springframework.orm.hibernate3.SessionFactoryUtils;   
  16. import org.springframework.orm.hibernate3.SessionHolder;   
  17. import org.springframework.transaction.support.TransactionSynchronizationManager;   
  18.   
  19. /**   
  20.  * <class>HibernateLazyResolver</class> 用于模拟 OpenSessionInViewInterceptor, 它可以被任意使用而不依赖于 Web 环境   
  21.  *    
  22.  * @see org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor   
  23.  * @see org.springframework.orm.hibernate3.support.OpenSessionInViewFilter   
  24.  * @since 2005-7-14   
  25.  * @author 王政   
  26.  * @version $Id: HibernateLazyResolver.java,v 1.4 2005/07/14 14:15:19 Administrator Exp $   
  27.  */   
  28. public class HibernateLazyResolver implements InitializingBean {   
  29.   
  30.     private static Log logger = LogFactory.getLog(HibernateLazyResolver.class);   
  31.   
  32.     private boolean singleSession = true;    
  33.   
  34.     private SessionFactory sessionFactory;   
  35.   
  36.     boolean participate = false;   
  37.   
  38.     protected Session session = null;   
  39.           
  40.     public final void setSessionFactory(SessionFactory sessionFactory) {   
  41.         this.sessionFactory = sessionFactory;   
  42.     }   
  43.   
  44.     /**   
  45.     * Set whether to use a single session for each request. Default is true.   
  46.     * <p>If set to false, each data access operation or transaction will use   
  47.     * its own session (like without Open Session in View). Each of those   
  48.     * sessions will be registered for deferred close, though, actually   
  49.     * processed at request completion.   
  50.     * @see SessionFactoryUtils#initDeferredClose   
  51.     * @see SessionFactoryUtils#processDeferredClose   
  52.     */   
  53.     public void setSingleSession(boolean singleSession) {   
  54.         this.singleSession = singleSession;   
  55.     }   
  56.   
  57.     /**   
  58.     * Return whether to use a single session for each request.   
  59.     */   
  60.     protected boolean isSingleSession() {   
  61.         return singleSession;   
  62.     }   
  63.        
  64.     public void afterPropertiesSet() throws Exception {   
  65.         if (sessionFactory == null) {   
  66.             throw new IllegalArgumentException("SessionFactory is reqirued!");   
  67.         }   
  68.     }   
  69.   
  70.     /**   
  71.      * 初始化 session, 在需要 lazy 的开始处调用   
  72.      *   
  73.      */   
  74.     public void openSession() {   
  75.         if (isSingleSession()) {   
  76.             // single session mode   
  77.             if (TransactionSynchronizationManager.hasResource(sessionFactory)) {   
  78.                 // Do not modify the Session: just set the participate flag.   
  79.                 participate = true;   
  80.             }   
  81.             else {   
  82.                 logger.debug("Opening single Hibernate Session in HibernateLazyResolver");   
  83.                 session = getSession(sessionFactory);   
  84.                 TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));   
  85.             }   
  86.         }   
  87.         else {   
  88.             // deferred close mode   
  89.             if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) {   
  90.                 // Do not modify deferred close: just set the participate flag.   
  91.                 participate = true;   
  92.             }   
  93.             else {   
  94.                 SessionFactoryUtils.initDeferredClose(sessionFactory);   
  95.             }   
  96.         }   
  97.            
  98.     }   
  99.   
  100.     /**   
  101.      * 释放 session, 在 lazy 的结束处调用   
  102.      *   
  103.      */   
  104.     public void releaseSession() {   
  105.         if (!participate) {   
  106.             if (isSingleSession()) {   
  107.                 // single session mode   
  108.                 TransactionSynchronizationManager.unbindResource(sessionFactory);   
  109.                 logger.debug("Closing single Hibernate Session in HibernateLazyResolver");   
  110.                 try {   
  111.                     closeSession(session, sessionFactory);   
  112.                 }   
  113.                 catch (RuntimeException ex) {   
  114.                     logger.error("Unexpected exception on closing Hibernate Session", ex);   
  115.                 }   
  116.             }   
  117.             else {   
  118.                 // deferred close mode   
  119.                 SessionFactoryUtils.processDeferredClose(sessionFactory);   
  120.             }   
  121.         }   
  122.     }   
  123.            
  124.     /**   
  125.      * Get a Session for the SessionFactory that this filter uses.   
  126.      * Note that this just applies in single session mode!   
  127.      * <p>The default implementation delegates to SessionFactoryUtils'   
  128.      * getSession method and sets the Session's flushMode to NEVER.   
  129.      * <p>Can be overridden in subclasses for creating a Session with a custom   
  130.      * entity interceptor or JDBC exception translator.   
  131.      * @param sessionFactory the SessionFactory that this filter uses   
  132.      * @return the Session to use   
  133.      * @throws DataAccessResourceFailureException if the Session could not be created   
  134.      * @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession(SessionFactory, boolean)   
  135.      * @see org.hibernate.FlushMode#NEVER   
  136.      */   
  137.     protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {   
  138.         Session session = SessionFactoryUtils.getSession(sessionFactory, true);   
  139.         // 注意这里与 OpenSessionInViewInterceptor 不同, 需要设置为 auto, 否则会导致以下异常   
  140.         // org.springframework.dao.InvalidDataAccessApiUsageException:    
  141.         // Write operations are not allowed in read-only mode (FlushMode.NEVER) -    
  142.         // turn your Session into FlushMode.AUTO or remove 'readOnly' marker from transaction definition   
  143.         session.setFlushMode(FlushMode.AUTO);   
  144.         return session;   
  145.     }   
  146.   
  147.     /**   
  148.      * Close the given Session.   
  149.      * Note that this just applies in single session mode!   
  150.      * <p>The default implementation delegates to SessionFactoryUtils'   
  151.      * releaseSession method.   
  152.      * <p>Can be overridden in subclasses, e.g. for flushing the Session before   
  153.      * closing it. See class-level javadoc for a discussion of flush handling.   
  154.      * Note that you should also override getSession accordingly, to set   
  155.      * the flush mode to something else than NEVER.   
  156.      * @param session the Session used for filtering   
  157.      * @param sessionFactory the SessionFactory that this filter uses   
  158.      */   
  159.     protected void closeSession(Session session, SessionFactory sessionFactory) {   
  160.         // 需要 flush session   
  161.         session.flush();   
  162.         SessionFactoryUtils.releaseSession(session, sessionFactory);   
  163.     }   
  164. }   

 

使用方法, 在配置文件中声明

 

代码
  1. <!-- use to resolve hibernate lazy load -->  
  2. <bean id="hibernateLazyResolver" class="org.summerfragrance.support.hibernate3.HibernateLazyResolver">  
  3.      <property name="sessionFactory"><ref local="sessionFactory"/></property>  
  4. </bean>    
  5.   
  6. <bean id="userManager" parent="txProxyTemplate">  
  7.        <property name="target">  
  8.            <bean class="org.summerfragrance.security.service.impl.UserManagerImpl" parent="managerTarget">  
  9.                <property name="userDAO"><ref bean="userDAO"/></property>  
  10.             <property name="hibernateLazyResolver"><ref bean="hibernateLazyResolver"/></property>  
  11.            </bean>  
  12.        </property>  
  13.    </bean>  

 

然后在代码中这样调用

 

代码
  1. hibernateLazyResolver.openSession();   
  2.   
  3. ...   
  4. //需要 lazy load 的代码   
  5.   
  6. hibernateLazyResolver.releaseSession();   

 

如果是 TestCase, 可以简单的设置 BaseTestCase 如下

代码
  1.   
  2. package org.summerfragrance;   
  3.   
  4. import junit.framework.TestCase;   
  5.   
  6. import org.apache.commons.logging.Log;   
  7. import org.apache.commons.logging.LogFactory;   
  8. import org.springframework.context.ApplicationContext;   
  9. import org.springframework.context.support.ClassPathXmlApplicationContext;   
  10. import org.summerfragrance.support.hibernate3.HibernateLazyResolver;   
  11.   
  12. /**  
  13.  * Base class for running DAO tests.  
  14.  *   
  15.  * @author mraible  
  16.  */  
  17. public class BaseTestCase extends TestCase {   
  18.   
  19.     protected final Log log = LogFactory.getLog(getClass());   
  20.   
  21.     protected final static ApplicationContext ctx;   
  22.   
  23.     protected HibernateLazyResolver hibernateLazyResolver;   
  24.   
  25.     static {   
  26.         String[] paths = { "/conf/applicationContext-dataSource.xml",   
  27.                 "/org/summerfragrance/vfs/applicationContext-vfs.xml",   
  28.                 "/org/summerfragrance/security/dao/hibernate/applicationContext-hibernate.xml"  
  29.         // "/org/summerfragrance/security/dao/jdbc/applicationContext-jdbc.xml"   
  30.         };   
  31.         ctx = new ClassPathXmlApplicationContext(paths);   
  32.     }   
  33.   
  34.     /**  
  35.      * @see junit.framework.TestCase#setUp()  
  36.      */  
  37.     protected void setUp() throws Exception {   
  38.         super.setUp();   
  39.         hibernateLazyResolver = (HibernateLazyResolver) ctx   
  40.                 .getBean("hibernateLazyResolver");   
  41.         hibernateLazyResolver.openSession();   
  42.     }   
  43.   
  44.     /**  
  45.      * @see junit.framework.TestCase#tearDown()  
  46.      */  
  47.     protected void tearDown() throws Exception {   
  48.         super.tearDown();   
  49.         hibernateLazyResolver.releaseSession();   
  50.         hibernateLazyResolver = null;   
  51.     }   
  52.   
  53. }   
  54.   

 

这样就可以在 Service 和 TestCase 中使用 Lazy Load 了, 目前已经测试通过

这几天看 JavaEye 上关于 OpenSessionInView 的讨论, 感觉这个问题比较常见

 

代码
  1.   
  2.   在代码中调用 openSession(), 然后不予处理, 这就是 ajoo 说的第一种不擦屁股就直接走人的做法, 这样可能导致两种错误;   
  3.    a. org.springframework.orm.hibernate3.HibernateSystemException: Illegal attempt to associate a collection with two open sessions   
  4.    b. 数据库连接不关闭   
  5.    正确的做法是用 HibernateCallBack 或者参照 HibernateTemplate 对 session 进行处理   
  6.   

 

以上都是一些个人想法, 小弟学习 Hibernate 不久, 欢迎各位拍转

 

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
spring的七大模块和使用spring框架的七种好处
目录 Spring框架模块: 1、核心容器(Core) 2、AOP模块 3、对象/关系映射集成模块ORM 4、JDBC抽象和DAO模块 5、Spring的Web模块 6、应用上下文(Context)模块 7、Spring的MVC框架 spring框架的好处: 第一: 非侵入式设计 第二:支持AOP 第三:支持声明式事务处理 第四:方便集成各种优秀框架 第五:降低Java EE API的使用难度 第六:方便解耦、简化开发 第七:方便程序的测试
0 0
后端开发必须知道的Spring框架基础模块大全
后端开发必须知道的Spring框架基础模块大全
0 0
Spring 加强版 ORM 框架 spring-data-jpa 入门与实践
前言 伴随着 Java 诞生与发展,目前 Java 界涌现出了五花八门的数据访问技术,有一些名词甚至达到了耳熟能详的程度,包括 JDBC、JTA、JPA、ORM、MyBatis 等等,这篇介绍的是 Spring Data 项目中的 spring-data-jpa 框架,了解其他相关技术可以查阅我前面的文章。
0 0
Spring 加强版 ORM 框架 spring-data-jdbc 入门与实践
前言 Spring 为了支持以统一的方式访问不同类型的数据库,提供了一个 Spring Data 框架,这个框架根据不同的数据库访问技术划分了不同的模块。上篇 《Spring 加强版 ORM 框架 Spring Data 入门》 介绍了不同模块遵循的通用规范,这篇我们来介绍下基于 JDBC 技术实现的 spring-data-jdbc 模块。
0 0
Spring 加强版 ORM 框架 Spring Data 入门
概述 Spring 中有多种操作数据库的方式,通常来说我们优先选择的是 MyBatis,如果业务比较简单我们还会使用 JdbcTemplate,另外据说国外使用 spring-data-jpa 比较多?
0 0
Spring 框架中的 @Enable* 注解是怎样实现的?
概述 Spring 的项目中,我们经常会使用 @Enable 开头的注解到配置类中,添加了这种注解之后,便会开启一些功能特性。常用的注解如 @EnableWebMvc、@EnableTransactionManagement、@EnableAsync、@EnableScheduling 等等。
0 0
【Spring系列】- 手写模拟Spring框架
上次已经学习了Java的设计模式,接下来就先来学习一下如何手写模拟简易的Spring,通过动手实践,才会更好的了解spring底层原理,今天就简单的模拟Spring容器是如何创建,bean又是如何注入的。
0 0
Spring框架中用到了那些设计模式????
Spring框架中用到了那些设计模式????
0 0
spring6详细教程(三)--->手写Spring框架
1. 在线学习链接:B站搜动力节点_老杜,观看完整教程!!! 2. 能够手写出spring框架 3. 回归了反射相关知识
0 0
+关注
风月无边
java,架构方面专家
文章
问答
文章排行榜
最热
最新
相关电子书
更多
Java Spring Boot开发实战系列课程【第6讲】:Spring Boot 2.0实战MyBatis与优化(Java面试题)
立即下载
Spring框架入门
立即下载
Java Spring Boot开发实战系列课程【第7讲】:Spring Boot 2.0安全机制与MVC身份验证实战(Java面试题)
立即下载