交易开始时的监听器

时间:2016-05-30 15:13:52

标签: java spring hibernate spring-transactions hibernate-session

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

一些事情:

@Component
class TransactionListener implements ?? {

    @Autowired
    private Something x;

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

}

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

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

3 个答案:

答案 0 :(得分:1)

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

据我所知,Spring在交易开始时不会让你知道,虽然这可能因不同的实施PlatformTransactionManager而异。如果你想挂钩Spring事务,我相信你留下了

  1. 对事务管理器进行子类化并调用一些回调
  2. 使用spring-aop为@Transactional创建建议(这显然只有在使用注释时才有效)
  3. 如果您正在使用Hibernate,那么https://docs.jboss.org/hibernate/core/3.6/javadocs/org/hibernate/Interceptor.html#afterTransactionBegin(org.hibernate.Transaction)

    中的afterTransactionBegin可能会有运气

答案 1 :(得分:1)

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

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

要做的第一件事是确定您正在使用哪个实现。在我们的例子中,它是在JpaTransactionManager类中声明的简单@Configuration,所以很简单。

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

如果这还不够,可以很容易地将其子类化并替换上一个。然后只需覆盖您要拦截的方法,例如doBegin()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()之后立即调用此方法。

答案 2 :(得分: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;
    }
}

或者,您可以使用Spring的SimpleTransactionScope并将bean作为事务范围。当其他人在事务中调用DealWithStuffPerTx.addMoreStuff(Object)时,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);
    }
}