事务内部`TransactionSynchronizationManager` afterCommit与TaskExecutor

时间:2017-02-15 19:29:18

标签: java spring hibernate spring-mvc transactions

我在交易中使用InfiniteLoaderTransactionSynchronization.afterCommit的代码:

TransactionSynchronizationManager.registerSynchronization(...)

在afterCommit代码中调用TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { @Override public void afterCommit() { Manager.save(param) } }); 的方法:

@Transactional

我使用Spring,Hibernate和EntityManagerFactory,这个代码在Spring MVC中运行时运行良好,否则,当发出Web请求但是在TaskExecutor(public class ManagerImpl implements Manager { @Transactional public void save(...) { ... } } )内运行时我得到错误:

org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor

我使用javax.persistence.TransactionRequiredException: no transaction is in progress at org.hibernate.jpa.spi.AbstractEntityManagerImpl.checkTransactionNeeded(AbstractEntityManagerImpl.java:1171) at org.hibernate.jpa.spi.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:1332) at sun.reflect.GeneratedMethodAccessor2083.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:293) at com.sun.proxy.$Proxy147.flush(Unknown Source) //called entityManager.flush() 检查了交易,然后返回true。

2 个答案:

答案 0 :(得分:0)

当调用#afterCommit()方法时,Spring仍然可以看到上下文绑定到现有事务,只是事务已经被调用;因此,为什么你看到它可能是活跃的。

根据方法here的javadoc,如果您想要允许其他代码运行需要事务,则需要强制Spring生成新的传播事务。因此,请按以下方式更改Manager#save方法:

public class ManagerImpl implements Manager {
  @Transactional(propagation = PROPAGATION_REQUIRES_NEW) 
  public void save( ... ) {
  }
}

答案 1 :(得分:0)

这仅在负责管理事务的 PlatformTransactionManager 类能够区分处于已完成状态的旧事务和在 TransactionSynchronization 中启动的新事务时才有效.

由于内部实现细节,像 PlatformTransactionManager 这样的

HibernateTransactionManager 类无法在所有情况下强制执行此操作。这主要是因为 HibernateTransactionManager 通过 Session 将 Hibernate TransactionSessionHolder 实例联系起来。 SessionHolder 实例缓存在线程局部变量中。在同一线程中启动新事务时,HibernateTransactionManager 检索与该线程关联的 SessionHolder,以重用事务和 Hibernate 会话。

在正常情况下,这不是什么大问题,因为线程本地缓存在事务提交或回滚时被清除。但是,在 TransactionSynchronization 中,线程本地值尚未清除,因此会阻止初始化新事务,除非明确进行初始化。

有很多方法可以解决这个问题:

  1. 使用 REQUIRES_NEW 的转换传播。这可能会被视为一种 hack,除非它是在 Facade 中的方法上指定的。它强制 Spring 事务方面开始一个新事务,绕过涉及上述边缘情况的问题。
  2. 在新线程中执行事务性工作。这确保线程本地缓存不会干扰启动新事务。可以使用带 @Async 注释的方法来包装作品。
  3. 将事务同步解耦,不要在事务同步适配器中执行任何工作。相反,发布一个事件并异步处理该事件 - 事务性工作可以在事件侦听器中完成。
相关问题