`
brandNewUser
  • 浏览: 446415 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Web项目中定时任务无法绑定SessionFactory的问题解决

 
阅读更多

 

正常我们在web开发中,由于需要在页面上或者脱离事务时使用到懒加载对应的对象,一般都采用Open Session In View模式。
 

Open Session In View

 
OpenSessionInView 模式用法探讨,在没有使用Spring提供的Open Session In View情况下,因需要在service(or Dao)层里把session关闭,所以lazy loading 为true的话,要在应用层内把关系集合都初始化,如company.getEmployees(),否则Hibernate抛session already closed Exception。 
 
Open Session In View提供了一种简便的方法,较好地解决了lazy loading问题。它有两种配置方式OpenSessionInViewInterceptor和OpenSessionInViewFilter(具体参看SpringSide),功能相同,只是一个在web.xml配置,另一个在application.xml配置而已。 
 
Open Session In View在request把session绑定到当前thread期间一直保持hibernate session在open状态,使session在request的整个期间都可以使用,如在View层里PO也可以lazy loading数据,如 ${ company.employees }。当View 层逻辑完成后,才会通过Filter的doFilter方法或Interceptor的postHandle方法自动关闭session。
 
Hibernate中自带了OpenSessionInViewFilter,我们可以直接在web.xml中对其进行配置:
<filter>
        <filter-name>openSessionInViewFilter</filter-name>
        <filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
        <init-param>
            <param-name>singleSession</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
<filter-mapping>
        <filter-name>openSessionInViewFilter</filter-name>
        <url-pattern>*.do</url-pattern>
    </filter-mapping>
 
 
 
这样就可以对所有的服务请求都进行过滤,并在其中绑定了SessionFactory,具体可以查看org.springframework.orm.hibernate4.support.OpenSessionInViewFilter源码。
 
由于定时任务(Cron)并不是通过请求过来的,不会走到Filter中,所以也不会在线程上下文中绑定sessionFactory,因此需要用其他的方式。如果使用OpenSessionInViewInterceptor绑定service,就会将所有的service再次重新绑定,也没有这个必要。
 
因此,这里我们仿照OpenSessionInViewInterceptor写了一个专门绑定到指定包的Interceptor来做这件事情,确定所有的cron服务都经过sessionFactory绑定即可。
 
@Component
@Aspect
public class OpenSessionInCronInterceptor {

    private static final Logger logger = Logger.getLogger(OpenSessionInCronInterceptor.class.getName());

    @Autowired
    private SessionFactory sessionFactory;

    @Pointcut("execution(void com.ejiapei.service.cron.*.work())")
    protected void definePointcut() {
    }

    @Before("definePointcut()")
    public void preHandle() throws DataAccessException {
        logger.info("Opening Hibernate Session in OpenSessionInViewInterceptor");
        Session session = openSession();
        TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
    }

    /**
     * Unbind the Hibernate <code>Session</code> from the thread and close it).
     *
     * @see org.springframework.transaction.support.TransactionSynchronizationManager
     */
    @After("definePointcut()")
    public void afterCompletion() throws DataAccessException {
        SessionHolder sessionHolder =
                (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
        logger.info("Closing Hibernate Session in OpenSessionInViewInterceptor");
        SessionFactoryUtils.closeSession(sessionHolder.getSession());
    }

    /**
     * Open a Session for the SessionFactory that this interceptor uses.
     * <p>The default implementation delegates to the
     * <code>SessionFactory.openSession</code> method and
     * sets the <code>Session</code>'s flush mode to "MANUAL".
     *
     * @return the Session to use
     * @throws org.springframework.dao.DataAccessResourceFailureException if the Session could not be created
     * @see org.hibernate.FlushMode#MANUAL
     */
    protected Session openSession() throws DataAccessResourceFailureException {
        try {
            Session session = SessionFactoryUtils.openSession(sessionFactory);
            session.setFlushMode(FlushMode.MANUAL);
            return session;
        } catch (HibernateException ex) {
            throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
        }
    }

}
 
首先,我们需要使用@Autowird从当前的Spring容器中获取到绑定的SessionFactory,用来创建Session,这部分与OpenSessionInView Filter/Interceptor的代码比较类似,只不过spring提供的这两个方式做的事情实际上比我们这里需要的还要多。
 
这样,我们就可以在开始执行定时任务之前使用绑定的SessionFactory去重新开启一个Session,并绑定至SessionHolder,也就是该线程上线文中,就避免了懒加载的问题。 
 
除此之外,还有一种比较暴力的方式,就是把所有懒加载的地方都修改成Lazy.EAGER,如果这样就改为非懒加载,但是可能会影响性能,因为影响了Hibernate访问数据库的方式。
 
 
 
 
 
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics