【问题标题】:Hibernate 5 and Transaction rollback detection with interceptorHibernate 5 和带有拦截器的事务回滚检测
【发布时间】:2016-06-07 08:23:36
【问题描述】:

我们有一个 Hibernate Interceptor 正在拦截 afterTransactionCommit 并检查 wasCommited() 的事务,但我们最近升级到 Hibernate 5.0.7.Final,并且 hibernate 5 不再有这个调用,当我们调用 @ 987654323@ 函数我们似乎只得到ACTIVENOT_ACTIVE 而不管事务状态如何。

我查看了afterTransactionBegin,交易标记为ACTIVE,这是预期的,在beforeTransactionCompletion 中仍标记为ACTIVE,这又是预期的,但随后在afterTransactionCommit 中标记为NOT_ACTIVE,这对我来说没有意义。我会期待COMMITTED, ROLLED_BACK, FAILED_COMMIT 之一。无论事务状态如何,我都会得到这个,即使我抛出一个导致回滚的异常,我仍然看不到NOT_ACTIVE以外的任何状态。

我们查找此信息的原因是确定我们是否需要将一些消息发布到队列中。基本上,如果未提交交易,请不要发布。截至目前,使用 Hibernate 5,我似乎无法找出如何以编程方式确定事务是否成功。

【问题讨论】:

    标签: java spring hibernate jpa transactions


    【解决方案1】:

    Hibernate 5 删除了在其拦截器中检测回滚的能力。相反,可以捕获该事务已回滚,并在未提交时推断回滚。

    例如:

    public class MyInterceptor extends EmptyInterceptor {
    .
    .
    .
    
    private static ThreadLocal<Boolean> wasCommited = new ThreadLocal();
    
    @Override
    public void beforeTransactionCompletion(Transaction tx) {
        // not called for rollback
        wasCommited.set(Boolean.TRUE);
    }
    
    
    @Override
    public void afterTransactionCompletion(Transaction tx) {
    
        if ( !Boolean.TRUE.equals(wasCommited.get()) ) {
            try {
               // handle transaction rolled back
            }
            finally {
                wasCommited.set(null);
            }
        }
    }
    }
    

    【讨论】:

    • 您的问题是什么?请说明您的问题是什么,并提供一些示例(如果可能)来说明您预计会发生什么。
    • @Tim 你也知道如果提交真的失败了会发生什么吗?
    • 我检查了这个解决方案不起作用,如果提交本身失败。在那种情况下afterTransactionCompletion 看到提交,那没有发生。
    • @vnov 你是对的,在提交失败时不会被调用。在下面看到您的解决方案。对于失败的提交 w/ JdbcResourceLocalTransactionCoordinatorImpl afterCompletion 也不会被调用。 :(
    • 也就是说,在提交失败时也不会调用 Synchronization.afterCompletion,所以这两种方案都不起作用。
    【解决方案2】:

    如果你使用 Spring 事务,你可以使用 TransactionSynchronization 来处理这类事情。例如:

    TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization(){
               void afterCommit() {
                    ..
               }
    });
    

    TransactionSynchronizationManager

    【讨论】:

      【解决方案3】:

      我成功使用了javax.transaction.Synchronization,它可以在交易中注册并收到正确的状态。可以使用 Hibernate 拦截器注册:

        @Override
        public void afterTransactionBegin(Transaction tx) {
          tx.registerSynchronization(new Synchronization() {
            @Override
            public void afterCompletion(int status) {
              // Here the status is correct
            }
          });
        }
      

      编辑:

      不幸的是,此解决方案仅在提交或回滚成功时才有效,否则不会调用回调。我最初一定是做错了测试。在JdbcResourceLocalTransactionCoordinatorImpl.TransactionDriverControlImpl的代码中可以看到。

      【讨论】:

        【解决方案4】:

        经过一番挣扎,我最终将vnov's answer 用于Hibernate 5 解决方案(以替换现在缺少的org.hibernate.Transaction.wsComitted() 方法)。这个答案更深入地阐述了问题的解决方案。我正在通过 Spring AOP 使用org.hibernate.EmptyInterceptor

        @Before("bean(emptyInterceptor) && 执行(公共 * *.afterTransactionBegin(..)) && args(tx)") 受保护的无效afterTransactionBegin(org.hibernate.Transaction tx){ tx.registerSynchronization(新 javax.transaction.Synchronization() { @覆盖 公共无效 beforeCompletion() { } @覆盖 public void afterCompletion(int status) { /* 状态是例如javax.transaction.Status.STATUS_COMMITTED 在这里做我的事, 即把身体的身体移到这里 afterTransactionCompletion(Transaction) 方法 */ } }); }

        JdbcResourceLocalTransactionCoordinatorImpl.TransactionDriverControlImpl类源我看到了:

        • 如果提交失败,将捕获 RuntimeException 并回滚事务。因此,在成功回滚的情况下,将使用 status == Status.STATUS_UNKNOWN 调用 Synchronization.afterCompletion(int) 方法。
        • 如果回滚失败出现异常,则根本不会调用Synchronization.afterCompletion(int)方法。

        编辑 (2021-11-24)

        由于我们需要一个同时考虑回滚失败可能性的解决方案,我们想出了这个解决方案(在回滚失败后也会调用afterTransactionCompletion 方法)。

        私有 ThreadLocal txCommitted = ThreadLocal.withInitial(() -> false); @Before("bean(emptyInterceptor) && 执行(公共 * *.afterTransactionBegin(..)) && args(tx)") 受保护的无效afterTransactionBegin(org.hibernate.Transaction tx){ tx.registerSynchronization(新同步(){ @覆盖 公共无效 beforeCompletion() { } @覆盖 public void afterCompletion(int status) { txCommitted.set(status == Status.STATUS_COMMITTED); } }); } @Before("bean(emptyInterceptor) && 执行(公共 * *.afterTransactionCompletion(..)) && args(tx)") 受保护的无效 afterTransactionCompletion(org.hibernate.Transaction tx){ 如果 (txCommitted.get()) { /* 在这里做我提交时的东西 */ } 别的 { /* 在这里做我其他的事情 */ } }

        【讨论】:

        • 嗨,我写这篇评论是为了问你是否仍然是这样看待解决方案的,如果你在写这篇文章后改变了什么。我自己也在解决类似的问题,而这个 stackoverflow 项目似乎确实是唯一进入它的东西。
        • 是的,自从我写了答案以来,我仍在使用这种方法。似乎适用于我们的用例。
        • 感谢您的回复。我无法让它适用于我的设置,尽管接受的答案,由 ThreadLocal 的 Tim Dugan 提供,乍一看似乎有效。虽然我还没有对此进行过深远的测试。
        • 稍后回来说:我又试了一次,现在可以了。我可能没有检查正确的状态。我现在看到你提到它是Status.STATUS_UNKNOWN,我进入源代码也发现了这一点。如果有回滚,它只是设置为...不完全是细化的,但我猜是好的...
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-01-22
        • 1970-01-01
        • 1970-01-01
        • 2011-02-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多