在Restful App中对异步调用进行乐观锁定后拦截@Transactional

时间:2016-03-22 15:27:34

标签: java spring transactions spring-aop optimistic-locking

我今天遇到的问题是如何在@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,看看它是否有用。

2 个答案:

答案 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