Hibernate 5 和带有拦截器的事务回滚检测
Hibernate 5 and Transaction rollback detection with interceptor
我们有一个 Hibernate 拦截器正在拦截 afterTransactionCommit
并检查 wasCommited()
的事务,但我们最近升级到 Hibernate 5.0。7.Final,hibernate 5 不再有这个调用,当我们调用 getStatus()
函数时,无论事务状态如何,我们似乎都只能得到 ACTIVE
或 NOT_ACTIVE
。
我查看了 afterTransactionBegin
并且交易被标记为 ACTIVE
,这是预期的,而在 beforeTransactionCompletion
中它仍然被标记为 ACTIVE
,这也是预期的,但随后在 afterTransactionCommit
中标记为 NOT_ACTIVE
,这对我来说没有意义。我本以为 COMMITTED, ROLLED_BACK, FAILED_COMMIT
之一。无论事务状态如何,我都会得到这个,即使我抛出导致回滚的异常,我仍然看不到 NOT_ACTIVE
.
以外的任何状态
我们查找此信息的原因是确定我们是否需要 post 将一些消息放入队列。基本上,如果事务未提交,则不要 post。截至目前使用 Hibernate 5,我似乎无法找到如何以编程方式确定事务是否成功。
如果您使用 Spring 交易,您可以使用 TransactionSynchronization
处理此类事务。例如:
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization(){
void afterCommit() {
..
}
});
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);
}
}
}
}
我成功使用了 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
.
的代码中看到
经过一番努力,我最终得到了 的 Hibernate 5 解决方案(以替换现在缺少的 org.hibernate.Transaction.wsComitted()
方法)。这个答案更深入地阐述了问题的解决方案。我通过 Spring AOP 使用 org.hibernate.EmptyInterceptor
。
@Before("bean(emptyInterceptor)
&& execution(public * *.afterTransactionBegin(..))
&& args(tx)")
protected void afterTransactionBegin(org.hibernate.Transaction tx) {
tx.registerSynchronization(new javax.transaction.Synchronization() {
@Override
public void beforeCompletion() {
}
@Override
public void afterCompletion(int status) {
/*
status is e.g. javax.transaction.Status.STATUS_COMMITTED
do my stuff here,
i.e. move here the body of the
afterTransactionCompletion(Transaction) method
*/
}
});
}
在
JdbcResourceLocalTransactionCoordinatorImpl.TransactionDriverControlImpl
class 来源 我看到了:
- 如果提交失败,将捕获RuntimeException 并回滚事务。因此,如果回滚成功,
Synchronization.afterCompletion(int)
方法将与 status == Status.STATUS_UNKNOWN
一起调用。
- 如果回滚异常失败,根本不会调用
Synchronization.afterCompletion(int)
方法
编辑 (2021-11-24)
由于我们需要一个同时考虑回滚失败可能性的解决方案,我们想出了这个解决方案(回滚失败后也会调用afterTransactionCompletion
方法)。
private ThreadLocal<Boolean> txCommitted = ThreadLocal.withInitial(() -> false);
@Before("bean(emptyInterceptor)
&& execution(public * *.afterTransactionBegin(..))
&& args(tx)")
protected void afterTransactionBegin(org.hibernate.Transaction tx) {
tx.registerSynchronization(new Synchronization() {
@Override
public void beforeCompletion() {
}
@Override
public void afterCompletion(int status) {
txCommitted.set(status == Status.STATUS_COMMITTED);
}
});
}
@Before("bean(emptyInterceptor)
&& execution(public * *.afterTransactionCompletion(..))
&& args(tx)")
protected void afterTransactionCompletion(org.hibernate.Transaction tx) {
if (txCommitted.get()) {
/* do my on-commit stuff here */
} else {
/* do my otherwise stuff here */
}
}
我们有一个 Hibernate 拦截器正在拦截 afterTransactionCommit
并检查 wasCommited()
的事务,但我们最近升级到 Hibernate 5.0。7.Final,hibernate 5 不再有这个调用,当我们调用 getStatus()
函数时,无论事务状态如何,我们似乎都只能得到 ACTIVE
或 NOT_ACTIVE
。
我查看了 afterTransactionBegin
并且交易被标记为 ACTIVE
,这是预期的,而在 beforeTransactionCompletion
中它仍然被标记为 ACTIVE
,这也是预期的,但随后在 afterTransactionCommit
中标记为 NOT_ACTIVE
,这对我来说没有意义。我本以为 COMMITTED, ROLLED_BACK, FAILED_COMMIT
之一。无论事务状态如何,我都会得到这个,即使我抛出导致回滚的异常,我仍然看不到 NOT_ACTIVE
.
我们查找此信息的原因是确定我们是否需要 post 将一些消息放入队列。基本上,如果事务未提交,则不要 post。截至目前使用 Hibernate 5,我似乎无法找到如何以编程方式确定事务是否成功。
如果您使用 Spring 交易,您可以使用 TransactionSynchronization
处理此类事务。例如:
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization(){
void afterCommit() {
..
}
});
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);
}
}
}
}
我成功使用了 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
.
经过一番努力,我最终得到了 org.hibernate.Transaction.wsComitted()
方法)。这个答案更深入地阐述了问题的解决方案。我通过 Spring AOP 使用 org.hibernate.EmptyInterceptor
。
@Before("bean(emptyInterceptor) && execution(public * *.afterTransactionBegin(..)) && args(tx)") protected void afterTransactionBegin(org.hibernate.Transaction tx) { tx.registerSynchronization(new javax.transaction.Synchronization() { @Override public void beforeCompletion() { } @Override public void afterCompletion(int status) { /* status is e.g. javax.transaction.Status.STATUS_COMMITTED do my stuff here, i.e. move here the body of the afterTransactionCompletion(Transaction) method */ } }); }
在
JdbcResourceLocalTransactionCoordinatorImpl.TransactionDriverControlImpl
class 来源 我看到了:
- 如果提交失败,将捕获RuntimeException 并回滚事务。因此,如果回滚成功,
Synchronization.afterCompletion(int)
方法将与status == Status.STATUS_UNKNOWN
一起调用。 - 如果回滚异常失败,根本不会调用
Synchronization.afterCompletion(int)
方法
编辑 (2021-11-24)
由于我们需要一个同时考虑回滚失败可能性的解决方案,我们想出了这个解决方案(回滚失败后也会调用afterTransactionCompletion
方法)。
private ThreadLocal<Boolean> txCommitted = ThreadLocal.withInitial(() -> false); @Before("bean(emptyInterceptor) && execution(public * *.afterTransactionBegin(..)) && args(tx)") protected void afterTransactionBegin(org.hibernate.Transaction tx) { tx.registerSynchronization(new Synchronization() { @Override public void beforeCompletion() { } @Override public void afterCompletion(int status) { txCommitted.set(status == Status.STATUS_COMMITTED); } }); } @Before("bean(emptyInterceptor) && execution(public * *.afterTransactionCompletion(..)) && args(tx)") protected void afterTransactionCompletion(org.hibernate.Transaction tx) { if (txCommitted.get()) { /* do my on-commit stuff here */ } else { /* do my otherwise stuff here */ } }