用 OpenSessionInViewInterceptor 的思路解决 Spring框架中的Hib

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

代码

[java] view plaincopy

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

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

[java] view plaincopy

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

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

代码

[html] view plaincopy

  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. <bean id="userManager" parent="txProxyTemplate">
  6. <property name="target">
  7. <bean class="org.summerfragrance.security.service.impl.UserManagerImpl" parent="managerTarget">
  8. <property name="userDAO"><ref bean="userDAO"/></property>
  9. <property name="hibernateLazyResolver"><ref bean="hibernateLazyResolver"/></property>
  10. </bean>
  11. </property>
  12. </bean>

然后在代码中这样调用

[java] view plaincopy

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

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

代码

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

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

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

代码

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

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

代码

用 OpenSessionInViewInterceptor 的思路解决 Spring框架中的Hib

时间: 2024-10-10 13:52:15

用 OpenSessionInViewInterceptor 的思路解决 Spring框架中的Hib的相关文章

再析在spring框架中解决多数据源的问题

在前面我写了<如何在spring框架中解决多数据源的问题>,通过设计模式中的Decorator模式在spring框架中解决多数据源的问题,得到了许多网友的关注.在与网友探讨该问题的过程中,我发现我的方案并不完善,它只解决了一部分问题. 总结多数据源的问题,其实它需要分为以下三种情况:各个数据源的数据结构不同.各个数据源的数据结构相同.各个数据源的数据结构部分相同又有部分不同.对于第二种情况,各个数据源的数据结构相同,我们使用一个sessionFactory,而在sessionFactory中通

spring框架中多数据源创建加载并且实现动态切换的配置实例代码

原文:spring框架中多数据源创建加载并且实现动态切换的配置实例代码 源代码下载地址:http://www.zuidaima.com/share/1774074130205696.htm 在我们的项目中遇到这样一个问题:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库.我们以往在spring和hibernate框架中总是配置一个数据源,因而sessionFactory的dataSource属性总是指向这个数据源并且恒定不变,所有DAO在使用sessionFa

细说shiro之五:在spring框架中集成shiro

官网:https://shiro.apache.org/ 1. 下载在Maven项目中的依赖配置如下: <!-- shiro配置 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>${version.shiro}</version> </dependency&g

【Spring】8、Spring框架中的单例Beans是线程安全的么

看到这样一个问题:spring框架中的单例Beans是线程安全的么? Spring框架并没有对单例bean进行任何多线程的封装处理.关于单例bean的线程安全和并发问题需要开发者自行去搞定.但实际上,大部分的Spring bean并没有可变的状态(比如Serview类和DAO类),所以在某种程度上说Spring的单例bean是线程安全的.如果你的bean有多种状态的话(比如 View Model 对象),就需要自行保证线程安全. 最浅显的解决办法就是将多态bean的作用域由"singleton&

Spring框架中2种生成代理对象的方法

Spring框架中2种生成代理对象的方法 Jdk Proxy基于接口生成代理对象,只能赋值给接口的引用(默认使用jdk). Spring进一步封装 CGLIB,基于实现类生成代理对象,既可以赋值给接口的引用,也可以赋值给实现类的引用 JDK提供的Proxy,和spring进一步封装的CGLIB.二者生成的代理没有任何区别,生成的都是代理对象.只是生产方式不同,前者是基于接口生成代理,后者基于实现类生成代理对象 如何切换spring框架中默认生成代理的方式 <aop:config proxy-ta

FAQ系列 | Spring框架中调用存储过程失败

Spring框架中,调用存储过程同时还需要show create procedure权限,对于普通用户而言,还要授予 select on mysql.proc 权限才能正常 --------------------------------------分割线-------------------------------------- 知数堂 (http://zhishuedu.com)培训是由资深MySQL专家叶金荣.吴炳锡联合推出的专业优质培训品牌,主要有MySQL DBA实战优化和Python

Spring框架中利用注解进行自动装配的环境配置步骤和常见问题

第1步:配置XML文件 ,如下: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.spring

3.Spring框架中的标签与配置文件分离

1.Spring框架中标签的配置 1. id属性和name属性的区别 * id -- Bean起个名字,在约束中采用ID的约束,唯一 * 取值要求:必须以字母开始,可以使用字母.数字.连字符.下划线.句话.冒号 id:不能出现特殊字符 * name -- Bean起个名字,没有采用ID的约束(基本不在使用) * 取值要求:name:出现特殊字符.如果<bean>没有id的话 , name可以当做id使用 * Spring框架在整合Struts1的框架的时候,Struts1的框架的访问路径是以/

Apache MINA框架整合到Spring框架中

毕业设计用到了Apache公司的MINA框架作为服务端与安卓客户端的通信框架. 问题:服务端分为两个部分,一个是基于javaEE平台的后台管理系统,另一个是基于MINA框架的服务,整个项目中需求是当tomcat服务器启动的时候,将MINA服务也一起启动起来,相当于服务端程序运行起来后,开启两个服务. 分析:服务端的后台管理系统是采用Spring+Hibernate框架整合搭建的,而当tomcat服务器启动后,会加载spring的配置文件,而spring配置文件中可以自定义监听器,将启动MINA服