4

我正在寻找一个干净的解决方案来让一个监听器开始事务。这意味着我希望侦听器成为 spring 上下文中的一个 bean(组件),它会在新事务启动时从 TransactionPlatformManager 或 Hibernate Session 或类似的东西接收事务启动事件。

伴随着一些东西:

@Component
class TransactionListener implements ?? {

    @Autowired
    private Something x;

    public void onTransactionBegin(...) {
        x.doSomething()
    }

}

具体来说,我正在缓解一个系统范围的问题,我需要在事务开始时设置一个本地线程,这样我就可以在处理休眠实体以检索信息时进一步访问该本地线程。

我查看了来源,并没有发现这样的听众可以实现的痕迹。我发现的唯一解决方案是继承 HibernateTransactionManager 及其 doBegin() 方法,我觉得这不是特别好。

4

3 回答 3

2

Spring 在其TransactionSynchronization中有一些事务回调,但是正如您正确注意到的那样,事务启动没有回调,我的错误。

据我所知,Spring 不会让您知道事务何时开始,尽管这可能因不同的实现而异PlatformTransactionManager。如果你想挂钩 Spring 事务,我相信你只剩下

  1. 子类化事务管理器并调用一些回调
  2. 使用 spring-aop创建一个建议@Transactional(显然,这仅在您使用注释时才有效)

如果您使用的是 Hibernate,您可能会afterTransactionBeginhttps://docs.jboss.org/hibernate/core/3.6/javadocs/org/hibernate/Interceptor.html#afterTransactionBegin(org.hibernate.Transaction)中获得一些运气

于 2016-05-30T15:30:00.647 回答
1

我有一个类似的问题,我想在事务开始后立即记录 Oracle 会话 ID,以便调查我们遇到的一些问题。

最后我发现,由于 Spring 使用PlatformTransactionManager,您可以通过自定义访问所有信息。

首先要做的是确定您正在使用哪个实现。在我们的例子中,它是一个类中的简单JpaTransactionManager声明@Configuration,因此相当容易。

完成此操作后,请注意您可以在此类上启用调试或跟踪日志记录,如果您的目标是调试问题,它已经提供了大量事务状态信息。

如果这还不够,很容易将其子类化并替换前一个。然后只需覆盖您要拦截的方法,例如doBegin()or prepareSynchronization()

例如看我的实现:

@Slf4j
public class LoggingJpaTransactionManager extends JpaTransactionManager {
    @Autowired
    private EntityManager entityManager;

    LoggingJpaTransactionManager(EntityManagerFactory emf) {
        super(emf);
    }

    @Override
    protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
        super.prepareSynchronization(status, definition);
        if (status.isNewTransaction() && log.isInfoEnabled()) {
            Query query = entityManager.createNativeQuery("select sys_context('USERENV','SID') from dual");
            Object sessionId = query.getSingleResult();
            log.info("Started a new transaction on session id {}", sessionId);
            TransactionSynchronizationManager.registerSynchronization(…);
        }
    }
}

注意:我选择覆盖prepareSynchronization()而不是doBegin()因为它允许使用TransactionSynchronizationManager,我认为通知提交/回滚事件仍然更清晰。doBegin()无论如何,此方法会立即被调用。

于 2018-12-07T18:30:26.553 回答
0

到目前为止,这对我有用。

@Aspect
@Component
public class StartTransactionInterceptor {

    @Pointcut("target(org.springframework.transaction.PlatformTransactionManager)")
    public void isPlatformTransactionManager() {
    }

    @Pointcut("execution(org.springframework.transaction.TransactionStatus getTransaction("
            + "org.springframework.transaction.TransactionDefinition)))")
    public void getsTransaction() {
    }

    @Around("isPlatformTransactionManager() && getsTransaction()")
    public Object registerSynchronization(ProceedingJoinPoint joinPoint) throws Throwable {
        TransactionStatus value = (TransactionStatus)joinPoint.proceed();
        if (value.isNewTransaction()) {
            // send some application event to others who are interested
        }
        return value;
    }
}

或者,您可以使用 SpringSimpleTransactionScope并将 bean 用于事务范围。DealWithStuffPerTx.addMoreStuff(Object)当其他人在事务中调用下面的 bean 时,bean 将被懒惰地实例化。

@Configuration
public class TransactionScopeConfig implements BeanFactoryPostProcessor {

    public static final String NAME = "tx";
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        beanFactory.registerScope(NAME, new SimpleTransactionScope());
    }
}

@Component
@Scope(value = TransactionScopeConfig.NAME, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class DealWithStuffPerTx extends TransactionSynchronizationAdapter {

    public void addMoreStuff(Object stuff) {
    }

    @Override
    public void afterCommit() {
        // deal with stuff
    }

    @PostConstruct
    public void init() {
        TransactionSynchronizationManager.registerSynchronization(this);
    }
}
于 2018-02-08T16:29:48.043 回答