一些事务传播不适用于Spring / Hibernate 4 [英] Some transaction propagations not working with Spring/Hibernate 4

查看:125
本文介绍了一些事务传播不适用于Spring / Hibernate 4的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在将应用程序从3.3升级到Hibernate 4.2。我们也在使用Spring 3.1.3(我们目前无法/不会更新)。



现在我的一些单元测试失败, / p>

  org.hibernate.HibernateException:找不到当前线程的会话


。这对于在错误的上下文中定义的< tx:annotation-driven /> 没有问题,或者是缺少CGLIB库的情况。大多数测试都能正常工作,这意味着在大多数情况下,事务代理正在工作。



现在失败的案例似乎围绕着使用NOT_SUPPORTED ,从不,和支持的传播类型。无论出于什么原因,SpringSessionContext都不会在这些情况下创建会话。



我们的用例有时需要事务边界不严格与方法边界对齐,会话有时会超过交易。在Spring 3 / Hibernate 3的情况下,会话上下文被绑定到本地线程,并且对 SessionFactory.getCurrentSession()的调用会返回一个会话实例,即使事务没有开始。这是我期待在Hibernate 4中仍然存在的行为。



有谁知道这个解决方法吗?如果Spring拒绝创建没有有效事务的会话,那么很难将会话边界与对话而不是事务对齐。一个Session和它的持久化上下文不应该绑定到一个打开的事务上。 解决方案

通过实现一个 CurrentSessionContext ,它是一个围绕 SpringSessionContext 的封装,并且借用了一些代码变化,使之成为Spring Framwork 4 +:

  public class ClassLoaderSpringSessionContext实现CurrentSessionContext {

private final SessionFactoryImplementor sessionFactory;
private final SpringSessionContext sessionContext;

public ClassLoaderSpringSessionContext(final SessionFactoryImplementor sessionFactory){
this.sessionFactory = sessionFactory; //这实际上是一些类加载逻辑,对于这个事务问题并不重要。
this.sessionContext = new SpringSessionContext(this.sessionFactory);

$ b @Override
public Session currentSession()throws HibernateException {
try {
return sessionContext.currentSession();
} catch(HibernateException e){
if(TransactionSynchronizationManager.isSynchronizationActive()){
Session session = this.sessionFactory.openSession();
if(TransactionSynchronizationManager.isCurrentTransactionReadOnly()){
session.setFlushMode(FlushMode.MANUAL);
}
SessionHolder sessionHolder = new SessionHolder(session);
TransactionSynchronizationManager
.registerSynchronization(新的SpringSessionSynchronization(sessionHolder,
this.sessionFactory));
TransactionSynchronizationManager.bindResource(this.sessionFactory,sessionHolder);
sessionHolder.setSynchronizedWithTransaction(true);
返回会话;
} else {
抛出新的HibernateException(
无法获取当前线程的事务同步会话);
}
}
}
}

SpringSessionSynchronization 是Spring 4中的一个包私有类,因此我还必须将它作为私有内部类的一个版本



希望这可以帮助别人。


I'm in the process of upgrading an application to Hibernate 4.2 from 3.3. We're also using Spring 3.1.3 (which we can't/won't update at this time).

Some of my unit tests are now failing with

org.hibernate.HibernateException: No Session found for current thread

in the SpringSessionContext. This is not an issue with the <tx:annotation-driven /> being defined in the wrong context, or a case of missing CGLIB libraries. Most of the tests do work, which means that in most cases, the transaction proxying is working.

The cases where it is now failing seem to be around the use of NOT_SUPPORTED, NEVER, and SUPPORTED propagation types. For whatever reason the SpringSessionContext doesn't create a session in these cases.

Our use cases sometimes require that transactional boundaries don't strictly line up with method boundaries, and that sessions sometimes outlive transactions. In the Spring 3/Hibernate 3 case, the session context was bound to a thread local, and a call to SessionFactory.getCurrentSession() would return a session instance even if a transaction was not started. This is the behavior that I am looking to still have in the Hibernate 4 case.

Does anyone know a workaround for this? It's tough to align Session boundaries with a conversation instead of a transaction if Spring refuses to create a session without a valid transaction. A Session and its persistence context shouldn't be tied to an open transaction.

解决方案

Worked around this issue by implementing a CurrentSessionContext that is a wrapper around a SpringSessionContext and borrowing some of the code changes that made it into Spring Framwork 4+:

public class ClassLoaderSpringSessionContext implements CurrentSessionContext {

    private final SessionFactoryImplementor sessionFactory;
    private final SpringSessionContext sessionContext;

    public ClassLoaderSpringSessionContext(final SessionFactoryImplementor sessionFactory) {
        this.sessionFactory = sessionFactory;  // This is actually some class loading logic that isn't important to this transaction problem.
        this.sessionContext = new SpringSessionContext(this.sessionFactory);
    }

    @Override
    public Session currentSession() throws HibernateException {
        try {
            return sessionContext.currentSession();
        } catch (HibernateException e) {
            if (TransactionSynchronizationManager.isSynchronizationActive()) {
                Session session = this.sessionFactory.openSession();
                if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
                    session.setFlushMode(FlushMode.MANUAL);
                }
                SessionHolder sessionHolder = new SessionHolder(session);
                TransactionSynchronizationManager
                        .registerSynchronization(new SpringSessionSynchronization(sessionHolder,
                            this.sessionFactory));
                TransactionSynchronizationManager.bindResource(this.sessionFactory, sessionHolder);
                sessionHolder.setSynchronizedWithTransaction(true);
                return session;
            } else {
                throw new HibernateException(
                        "Could not obtain transaction-synchronized Session for current thread");
            }
        }
    }
}

SpringSessionSynchronization was a package private class in Spring 4, so I also had to pull a version of that as a private inner class to ClassLoaderSpringSessionContext.

Hope this helps someone else.

这篇关于一些事务传播不适用于Spring / Hibernate 4的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆