zl程序教程

您现在的位置是:首页 >  后端

当前栏目

用 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"  

这样就可以在 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仓库 立即下载