用 OpenSessionInViewInterceptor 的思路解决 Spring框架中的Hibernate Lazy
2023-09-27 14:24:59 时间
众所周知, 为了解决 Hibernate Lazy 问题, Spring 中引入了 OpenSessionInViewInterceptor, 这样虽然解决了页面上的 Lazy Load 问题,却增加了各层之间的偶合性, 如果一个 Lazy 的 Collection 在页面上可以被正确的 load, 但是如果请求不是来自于 HttpServletRequest (比如在 TestCase 或
1. 设置了 lazy = "true" 会导致 org.hibernate.LazyInitializationException: failed to lazily initialize a collection of xxx: xxx - no session or session was closed 2. 设置里 lazy = "false" 会导致 org.hibernate.LazyInitializationException: could not initialize proxy - the owning Session was closed
import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.FlushMode; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.orm.hibernate3.SessionFactoryUtils; import org.springframework.orm.hibernate3.SessionHolder; import org.springframework.transaction.support.TransactionSynchronizationManager; /** * class HibernateLazyResolver /class 用于模拟 OpenSessionInViewInterceptor, 它可以被任意使用而不依赖于 Web 环境 * * @see org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor * @see org.springframework.orm.hibernate3.support.OpenSessionInViewFilter * @since 2005-7-14 * @author 王政 * @version $Id: HibernateLazyResolver.java,v 1.4 2005/07/14 14:15:19 Administrator Exp $ */ public class HibernateLazyResolver implements InitializingBean { private static Log logger = LogFactory.getLog(HibernateLazyResolver.class); private boolean singleSession = true; private SessionFactory sessionFactory; boolean participate = false; protected Session session = null; public final void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } /** * Set whether to use a single session for each request. Default is true. * p If set to false, each data access operation or transaction will use * its own session (like without Open Session in View). Each of those * sessions will be registered for deferred close, though, actually * processed at request completion. * @see SessionFactoryUtils#initDeferredClose * @see SessionFactoryUtils#processDeferredClose */ public void setSingleSession(boolean singleSession) { this.singleSession = singleSession; } /** * Return whether to use a single session for each request. */ protected boolean isSingleSession() { return singleSession; } public void afterPropertiesSet() throws Exception { if (sessionFactory == null) { throw new IllegalArgumentException("SessionFactory is reqirued!"); } } /** * 初始化 session, 在需要 lazy 的开始处调用 * */ public void openSession() { if (isSingleSession()) { // single session mode if (TransactionSynchronizationManager.hasResource(sessionFactory)) { // Do not modify the Session: just set the participate flag. participate = true; } else { logger.debug("Opening single Hibernate Session in HibernateLazyResolver"); session = getSession(sessionFactory); TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session)); } } else { // deferred close mode if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) { // Do not modify deferred close: just set the participate flag. participate = true; } else { SessionFactoryUtils.initDeferredClose(sessionFactory); } } } /** * 释放 session, 在 lazy 的结束处调用 * */ public void releaseSession() { if (!participate) { if (isSingleSession()) { // single session mode TransactionSynchronizationManager.unbindResource(sessionFactory); logger.debug("Closing single Hibernate Session in HibernateLazyResolver"); try { closeSession(session, sessionFactory); } catch (RuntimeException ex) { logger.error("Unexpected exception on closing Hibernate Session", ex); } } else { // deferred close mode SessionFactoryUtils.processDeferredClose(sessionFactory); } } } /** * Get a Session for the SessionFactory that this filter uses. * Note that this just applies in single session mode! * p The default implementation delegates to SessionFactoryUtils * getSession method and sets the Sessions flushMode to NEVER. * p Can be overridden in subclasses for creating a Session with a custom * entity interceptor or JDBC exception translator. * @param sessionFactory the SessionFactory that this filter uses * @return the Session to use * @throws DataAccessResourceFailureException if the Session could not be created * @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession(SessionFactory, boolean) * @see org.hibernate.FlushMode#NEVER */ protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException { Session session = SessionFactoryUtils.getSession(sessionFactory, true); // 注意这里与 OpenSessionInViewInterceptor 不同, 需要设置为 auto, 否则会导致以下异常 // org.springframework.dao.InvalidDataAccessApiUsageException: // Write operations are not allowed in read-only mode (FlushMode.NEVER) - // turn your Session into FlushMode.AUTO or remove readOnly marker from transaction definition session.setFlushMode(FlushMode.AUTO); return session; } /** * Close the given Session. * Note that this just applies in single session mode! * p The default implementation delegates to SessionFactoryUtils * releaseSession method. * p Can be overridden in subclasses, e.g. for flushing the Session before * closing it. See class-level javadoc for a discussion of flush handling. * Note that you should also override getSession accordingly, to set * the flush mode to something else than NEVER. * @param session the Session used for filtering * @param sessionFactory the SessionFactory that this filter uses */ protected void closeSession(Session session, SessionFactory sessionFactory) { // 需要 flush session session.flush(); SessionFactoryUtils.releaseSession(session, sessionFactory); } }
property name="hibernateLazyResolver" ref bean="hibernateLazyResolver"/ /property
"/org/summerfragrance/security/dao/hibernate/applicationContext-hibernate.xml"
在代码中调用 openSession(), 然后不予处理, 这就是 ajoo 说的第一种不擦屁股就直接走人的做法, 这样可能导致两种错误; a. org.springframework.orm.hibernate3.HibernateSystemException: Illegal attempt to associate a collection with two open sessions b. 数据库连接不关闭 正确的做法是用 HibernateCallBack 或者参照 HibernateTemplate 对 session 进行处理
Struts2+Spring+Hibernate整合步骤 Struts2+Spring+Hibernate是J2EE的最新流行框架。本篇是我搭建这个框架的经验总结。 本方案采用自底向上:Hibernate Spring Struts2的方式进行整合。具体步骤如下: 1.通过MyEclipse向导,导入实现Hibernate3.2功能:生成会话工厂和hibernate.cfg.xml文件。然后在src中添加log4j.properties文件。 2.通过MyEclipse向导,导入实现Spring2.5功能,生成applicationContext.xml文件。然后将Spring2.0 AOP Liberaries里的asm2.2.3.jar包
阿里特邀专家徐雷Java Spring Boot开发实战系列课程(第18讲):制作Java Docker镜像与推送到DockerHub和阿里云Docker仓库 立即下载
1. 设置了 lazy = "true" 会导致 org.hibernate.LazyInitializationException: failed to lazily initialize a collection of xxx: xxx - no session or session was closed 2. 设置里 lazy = "false" 会导致 org.hibernate.LazyInitializationException: could not initialize proxy - the owning Session was closed
import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.FlushMode; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.orm.hibernate3.SessionFactoryUtils; import org.springframework.orm.hibernate3.SessionHolder; import org.springframework.transaction.support.TransactionSynchronizationManager; /** * class HibernateLazyResolver /class 用于模拟 OpenSessionInViewInterceptor, 它可以被任意使用而不依赖于 Web 环境 * * @see org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor * @see org.springframework.orm.hibernate3.support.OpenSessionInViewFilter * @since 2005-7-14 * @author 王政 * @version $Id: HibernateLazyResolver.java,v 1.4 2005/07/14 14:15:19 Administrator Exp $ */ public class HibernateLazyResolver implements InitializingBean { private static Log logger = LogFactory.getLog(HibernateLazyResolver.class); private boolean singleSession = true; private SessionFactory sessionFactory; boolean participate = false; protected Session session = null; public final void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } /** * Set whether to use a single session for each request. Default is true. * p If set to false, each data access operation or transaction will use * its own session (like without Open Session in View). Each of those * sessions will be registered for deferred close, though, actually * processed at request completion. * @see SessionFactoryUtils#initDeferredClose * @see SessionFactoryUtils#processDeferredClose */ public void setSingleSession(boolean singleSession) { this.singleSession = singleSession; } /** * Return whether to use a single session for each request. */ protected boolean isSingleSession() { return singleSession; } public void afterPropertiesSet() throws Exception { if (sessionFactory == null) { throw new IllegalArgumentException("SessionFactory is reqirued!"); } } /** * 初始化 session, 在需要 lazy 的开始处调用 * */ public void openSession() { if (isSingleSession()) { // single session mode if (TransactionSynchronizationManager.hasResource(sessionFactory)) { // Do not modify the Session: just set the participate flag. participate = true; } else { logger.debug("Opening single Hibernate Session in HibernateLazyResolver"); session = getSession(sessionFactory); TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session)); } } else { // deferred close mode if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) { // Do not modify deferred close: just set the participate flag. participate = true; } else { SessionFactoryUtils.initDeferredClose(sessionFactory); } } } /** * 释放 session, 在 lazy 的结束处调用 * */ public void releaseSession() { if (!participate) { if (isSingleSession()) { // single session mode TransactionSynchronizationManager.unbindResource(sessionFactory); logger.debug("Closing single Hibernate Session in HibernateLazyResolver"); try { closeSession(session, sessionFactory); } catch (RuntimeException ex) { logger.error("Unexpected exception on closing Hibernate Session", ex); } } else { // deferred close mode SessionFactoryUtils.processDeferredClose(sessionFactory); } } } /** * Get a Session for the SessionFactory that this filter uses. * Note that this just applies in single session mode! * p The default implementation delegates to SessionFactoryUtils * getSession method and sets the Sessions flushMode to NEVER. * p Can be overridden in subclasses for creating a Session with a custom * entity interceptor or JDBC exception translator. * @param sessionFactory the SessionFactory that this filter uses * @return the Session to use * @throws DataAccessResourceFailureException if the Session could not be created * @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession(SessionFactory, boolean) * @see org.hibernate.FlushMode#NEVER */ protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException { Session session = SessionFactoryUtils.getSession(sessionFactory, true); // 注意这里与 OpenSessionInViewInterceptor 不同, 需要设置为 auto, 否则会导致以下异常 // org.springframework.dao.InvalidDataAccessApiUsageException: // Write operations are not allowed in read-only mode (FlushMode.NEVER) - // turn your Session into FlushMode.AUTO or remove readOnly marker from transaction definition session.setFlushMode(FlushMode.AUTO); return session; } /** * Close the given Session. * Note that this just applies in single session mode! * p The default implementation delegates to SessionFactoryUtils * releaseSession method. * p Can be overridden in subclasses, e.g. for flushing the Session before * closing it. See class-level javadoc for a discussion of flush handling. * Note that you should also override getSession accordingly, to set * the flush mode to something else than NEVER. * @param session the Session used for filtering * @param sessionFactory the SessionFactory that this filter uses */ protected void closeSession(Session session, SessionFactory sessionFactory) { // 需要 flush session session.flush(); SessionFactoryUtils.releaseSession(session, sessionFactory); } }
property name="hibernateLazyResolver" ref bean="hibernateLazyResolver"/ /property
"/org/summerfragrance/security/dao/hibernate/applicationContext-hibernate.xml"
这样就可以在 Service 和 TestCase 中使用 Lazy Load 了, 目前已经测试通过
这几天看 JavaEye 上关于 OpenSessionInView 的讨论, 感觉这个问题比较常见
在代码中调用 openSession(), 然后不予处理, 这就是 ajoo 说的第一种不擦屁股就直接走人的做法, 这样可能导致两种错误; a. org.springframework.orm.hibernate3.HibernateSystemException: Illegal attempt to associate a collection with two open sessions b. 数据库连接不关闭 正确的做法是用 HibernateCallBack 或者参照 HibernateTemplate 对 session 进行处理
Struts2+Spring+Hibernate整合步骤 Struts2+Spring+Hibernate是J2EE的最新流行框架。本篇是我搭建这个框架的经验总结。 本方案采用自底向上:Hibernate Spring Struts2的方式进行整合。具体步骤如下: 1.通过MyEclipse向导,导入实现Hibernate3.2功能:生成会话工厂和hibernate.cfg.xml文件。然后在src中添加log4j.properties文件。 2.通过MyEclipse向导,导入实现Spring2.5功能,生成applicationContext.xml文件。然后将Spring2.0 AOP Liberaries里的asm2.2.3.jar包
阿里特邀专家徐雷Java Spring Boot开发实战系列课程(第18讲):制作Java Docker镜像与推送到DockerHub和阿里云Docker仓库 立即下载
相关文章
- 8 -- 深入使用Spring -- 3... 资源访问
- Spring Boot整合OpenOffice实现Word、Excel、PPT在线预览
- 【Spring源码】Spring事务原理
- Spring官网改版后下载
- Spring Security 实战干货:动态权限控制(上)思路
- Spring Security 实战干货:实现自定义退出登录
- 全网首发 Spring Cloud Gateway 添加统一前缀思路探讨
- Spring Boot—19Session
- postman传递对象到spring controller的方式
- Spring Security 与 OAuth2(完整案例)
- spring-基于SpringBoot使用AOP技术实现操作日志管理
- Java中核心注解的作用及其使用,了解Spring容器装载的过程和机制,自定义注解来实现自动配置项目依赖环境,包括mybatis、Dubbo、log4j、RabbitMQ、redis相关等自动配置
- Spring Bean的生命周期例子