Intercepting @Transactional After Optimistic Lock for Asynchronous Calls in Restful App

一世执手 提交于 2020-01-14 06:22:08

问题


The question I have today is how to retry a method after the @Transactional annotation causes an Optimistic Lock Exception (OLE) and rolls back the transaction.

I have asynchronous calls to a Restful application that are attempting to update a database object based on some business logic. If I get an OLE, I'd like to retry the transaction after a delay of 0.2-0.5 seconds.

@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();
}

I've tried using AspectJ to intercept my method after it throws the OLE so that I can retry. However, the issue is the @Transactional annotation. My method is not throwing the error message since business logic is not failing. Instead, myMethod returns a 200 response, but the OLE exception is encountered and then thrown in the ResourceJavaMethodDispatcher.java class that is responsible for invoking myMethod.

My aspect 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;
    }
}

The invokeAndRetry() method has the logic to call wait on the thread and then retry up to a maximum of three tries.

I can successfully get into myAspect from an exception thrown by business logic; but the OLE thrown from the transaction does not get caught in myAspect.

Having said all of that, is there a way to wrap/encapsulate/intercept the @Transaction annotation in order to run my retry logic?

Side notes:

1) I've looked into creating my own @Retry annotation based on the example here. I've used that dependency to try his @Retry annotation, but to no avail.

2) I'll be looking into Spring's @within to see if that could prove useful.


回答1:


The short answer is: you shouldn't try to reuse an EntityManager after an exception occurs. According to the Hibernate EntityManager User guide on Transactions and concurrency, which most probably applies to all JPA providers:

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.

You might be able to do the retry the operation in a new transaction with a new instance of an EntityManager though, but that's a different use-case.




回答2:


After doing some research and looking at some more tutorials, I found a way to have my aspect take precedence over @Transactional. Just below the @Aspect tag, I added the annotation @Order(1).

This gives my aspect higher priority since @Transactional is defaulted to Ordered.LOWEST_PRECEDENCE. See Spring documentation for some more details about @Order.



来源:https://stackoverflow.com/questions/36158544/intercepting-transactional-after-optimistic-lock-for-asynchronous-calls-in-rest

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!