如何在Spring中重新启动死锁/锁超时事务?

26

当使用Spring(特别是Spring推荐的声明式事务)时,如何在死锁或锁定超时异常发生时实施事务重启的最佳做法是什么?

谢谢,

Asaf

4个回答

16

我觉得春季本身应该有一个好的答案来回答这个问题(至少以文档的形式,或是某种重试拦截器的形式)。然而,它没有。

如果你想继续保持“声明式”的风格处理重试,最好的方法可能是编写自己的拦截器实现,该实现将自动重试事务一定次数。首先,学习Spring的TransactionInterceptor,该拦截器管理声明性事务的开始/回滚/提交行为。如果正在使用Hibernate,请注意其如何处理Hibernate会话与当前线程的绑定/解绑。

如果您正在使用Hibernate,需要注意以下事项:

  • 您的“重试拦截器”确保解除任何预先存在的线程绑定的Hibernate会话并重新绑定一个新的会话。一旦在Hibernate/JDBC代码内部引发异常(例如死锁),相应的Hibernate会话就会被毒化,并且需要被丢弃。(session.clear()不足够)
  • 如果您的事务性服务方法使用Hibernate会话对象作为方法参数,请注意。在重试时,当您重置Hibernate会话时,这些对象将变成分离状态。如果服务方法假设它们被附加(例如,如果它们使用在服务方法中访问的惰性加载属性,或者如果您尝试保存它们等),则需要重新附加它们。一般来说,最好不要将Hibernate对象用作事务性服务方法的参数。
  • 您将实现MethodInterceptor.invoke() - 传递给此方法的MethodInvocation实例可能具有状态;在拦截器中使用它之前可能需要进行克隆。

8

我建议使用spring retry 项目 中的类org.springframework.retry.interceptor.RetryOperationsInterceptor,并像这样配置:

<aop:config>
    <aop:pointcut id="transactional" expression="execution(* com...*Service.remoteCall(..))" />
    <aop:advisor pointcut-ref="transactional" advice-ref="retryAdvice" order="-1"/>
</aop:config>

<bean id="retryAdvice" class="org.springframework.retry.interceptor.RetryOperationsInterceptor"/>

但是如果您仍希望自己实现它,来自Spring文档的AOP示例是一个很好的开始。


2
感谢提到spring-retry项目。 - Vineet Bhatia

4

几年前我也有同样的问题,并最终编写了自己的解决方案,作为一个AOP切面,在您的代码中看起来像这样:

  @RetryTransaction
  @Transactional
  public void doSomething() {
      ....
  }

4

没有通用的答案,因为它取决于应用程序的具体情况。例如,您可能希望执行自动事务操作重启或通知用户有关操作失败并要求明确的重试确认等。

如果是自动重启方案,我会使用AOP。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接