Restful 应用中异步调用乐观锁后拦截@Transactional
Intercepting @Transactional After Optimistic Lock for Asynchronous Calls in Restful App
我今天的问题是如何在@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();
}
我试过在我的方法抛出 OLE 后使用 AspectJ 拦截它,以便我可以重试。但是,问题是@Transactional 注释。我的方法是 not 抛出错误消息,因为业务逻辑没有失败。相反,myMethod returns 一个 200 响应,但是遇到 OLE 异常然后抛出在 ResourceJavaMethodDispatcher.java class 负责调用 myMethod.
我的方面class:
@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() 方法具有在线程上调用等待然后重试最多 3 次的逻辑。
我可以从业务逻辑抛出的异常中成功进入myAspect;但是从事务中抛出的 OLE 不会被 myAspect 捕获。
说了这么多,有没有办法 wrap/encapsulate/intercept @Transaction 注释以便 运行 我的重试逻辑?
旁注:
1) 我研究过根据示例 here 创建自己的 @Retry 注释。我已经使用该依赖项尝试了他的 @Retry 注释,但无济于事。
2) 我会研究 Spring 的 @within 看看它是否有用。
简短的回答是:您不应该在异常发生后尝试重用 EntityManager
。根据 Hibernate EntityManager User guide on Transactions and concurrency,这很可能适用于所有 JPA 提供程序:
If the EntityManager
throws an exception (including any SQLException
), you should immediately rollback the database transaction, call EntityManager.close()
(if createEntityManager()
has been called) and discard the EntityManager
instance. Certain methods of EntityManager
will not leave the persistence context in a consistent state. No exception thrown by an entity manager can be treated as recoverable. Ensure that the EntityManager
will be closed by calling close()
in a finally block. Note that a container managed entity manager will do that for you. You just have to let the RuntimeException
propagate up to the container.
虽然您可以使用 EntityManager
的新实例在新事务中重试该操作,但这是不同的用例。
在做了一些研究并查看了更多教程之后,我找到了一种让方面优先于 @Transactional 的方法。在@Aspect 标签下方,我添加了注释@Order(1).
由于@Transactional 默认为 Ordered.LOWEST_PRECEDENCE,这使我的方面具有更高的优先级。有关 @Order 的更多详细信息,请参阅 Spring documentation。
我今天的问题是如何在@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();
}
我试过在我的方法抛出 OLE 后使用 AspectJ 拦截它,以便我可以重试。但是,问题是@Transactional 注释。我的方法是 not 抛出错误消息,因为业务逻辑没有失败。相反,myMethod returns 一个 200 响应,但是遇到 OLE 异常然后抛出在 ResourceJavaMethodDispatcher.java class 负责调用 myMethod.
我的方面class:
@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() 方法具有在线程上调用等待然后重试最多 3 次的逻辑。
我可以从业务逻辑抛出的异常中成功进入myAspect;但是从事务中抛出的 OLE 不会被 myAspect 捕获。
说了这么多,有没有办法 wrap/encapsulate/intercept @Transaction 注释以便 运行 我的重试逻辑?
旁注:
1) 我研究过根据示例 here 创建自己的 @Retry 注释。我已经使用该依赖项尝试了他的 @Retry 注释,但无济于事。
2) 我会研究 Spring 的 @within 看看它是否有用。
简短的回答是:您不应该在异常发生后尝试重用 EntityManager
。根据 Hibernate EntityManager User guide on Transactions and concurrency,这很可能适用于所有 JPA 提供程序:
If the
EntityManager
throws an exception (including anySQLException
), you should immediately rollback the database transaction, callEntityManager.close()
(ifcreateEntityManager()
has been called) and discard theEntityManager
instance. Certain methods ofEntityManager
will not leave the persistence context in a consistent state. No exception thrown by an entity manager can be treated as recoverable. Ensure that theEntityManager
will be closed by callingclose()
in a finally block. Note that a container managed entity manager will do that for you. You just have to let theRuntimeException
propagate up to the container.
虽然您可以使用 EntityManager
的新实例在新事务中重试该操作,但这是不同的用例。
在做了一些研究并查看了更多教程之后,我找到了一种让方面优先于 @Transactional 的方法。在@Aspect 标签下方,我添加了注释@Order(1).
由于@Transactional 默认为 Ordered.LOWEST_PRECEDENCE,这使我的方面具有更高的优先级。有关 @Order 的更多详细信息,请参阅 Spring documentation。