我今天遇到的问题是如何在@Transactional注释导致乐观锁定异常(OLE)并回滚事务后重试方法。
我对Restful应用程序进行异步调用,尝试根据某些业务逻辑更新数据库对象。如果我得到一个OLE,我想在0.2-0.5秒的延迟后重试该事务。
@Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRED, readOnly = false)
public Response myMethod(Long myParam) throws Exception {
~Call to update db using hibernate after business logic~;
return Response.ok().build();
}
我尝试使用AspectJ在抛出OLE后拦截我的方法,以便我可以重试。但问题是@Transactional注释。我的方法是不抛出错误消息,因为业务逻辑没有失败。相反,myMethod返回200响应,但遇到OLE异常,然后在负责调用myMethod的ResourceJavaMethodDispatcher.java类中抛出。
我的方面课程:
@Aspect
public class myAspect {
@AfterThrowing(value = "execution(* com.package.blah.myClass.myMethod(..)) && args(.., myParam)", throwing = "ex")
public Response catchAndRetry(JoinPoint jp, Throwable ex, Long myParam) throws Throwable {
Response response = null;
response = invokeAndRetry(jp, myParam);
return response;
}
}
invokeAndRetry()方法具有在线程上调用wait的逻辑,然后最多重试三次。
我可以从业务逻辑抛出的异常中成功进入myAspect;但是从事务中抛出的OLE不会被myAspect捕获。
说完所有这些之后,是否有办法包装/封装/拦截@Transaction注释以运行我的重试逻辑?
附注:
1)我已经考虑根据示例here创建自己的@Retry注释。我已经使用该依赖来尝试他的@Retry注释,但无济于事。
2)我将调查Spring的@within,看看它是否有用。
答案 0 :(得分:0)
简短的回答是:您不应该在发生异常后尝试重用EntityManager
。根据{{3}},最有可能适用于所有JPA提供者:
如果
EntityManager
抛出异常(包括任何SQLException
),则应立即回滚数据库事务,调用EntityManager.close()
(如果已调用createEntityManager()
)并丢弃EntityManager
实例。EntityManager
的某些方法不会使持久化上下文保持一致状态。实体管理器抛出的异常不能被视为可恢复的。确保通过在finally块中调用EntityManager
来关闭close()
。请注意,容器管理实体管理器将为您执行此操作。您只需让RuntimeException
传播到容器。
您可以使用EntityManager
的新实例在新事务中重试该操作,但这是一个不同的用例。
答案 1 :(得分:0)
在做了一些研究并查看了一些教程之后,我找到了一种让我的方面优先于@Transactional的方法。在@Aspect标签下方,我添加了注释@Order(1)。
这使我的方面具有更高的优先级,因为@Transactional默认为Ordered.LOWEST_PRECEDENCE。有关@Order的更多详细信息,请参阅Spring documentation。