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.
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.
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.
Related
I'm using Spring Boot 2.0.1 with inclusion of validation-starter (which comes with Hibernate Validation 6.x) and trying to validate a method parameter and return objects in my Spring repository layer. There are basically a ParamObj and ReturnObj for this method,
#Repository
#Validated // Spring integration of validation
public class MyRepoImpl implements MyRepo {
#Override
#Valid
public ReturnObj myMethod(#Valid ParamObj param) throw CustomException {
try {
// makes a call that might throw an exception
} catch (Exception e) {
throw new CustomException(/*...*/, e);
}
return returnObj;
}
}
ParamObj and ReturnObj both use a simple set of Bean Validation annotations on some of their properties, e.g #Pattern, #Min, etc.. My problem is that with these annotations, if either ParamObj or ReturnObj fails the validation process, MyRepoImpl#myMethod() would throw javax.validation.ConstraintViolationException.
However, as you can see, the contract for myMethod() mandates to throw a CustomException. Yes, ConstraintViolationException is a RuntimeException, however, now the caller of MyRepoImpl#myMethod(), e.g. a service layer logic, needs to catch this RuntimeException in addition to CustomException.
And the reason is, I need to prevent all the exceptions from bubbling out of the service layer (for this particular Spring #Service I have which calls this repository) -- this service is part of a long list of services that are called via a service orchestration which constructs a larger encompassing object. In other word, this piece (result coming obtained via MyRepoImpl) is considered optional, and subsequently, its failure should only be logged but do not stop the whole process.
For that reason, I can't utilize ControllerAdvice to capture ConstraintViolationException for the entire application.
My goal is to be able, when the validation fails for either method parameter or return, to throw only CustomException back to the caller.
Is this possible with annotations only? I looked into MethodValidationPostProcessor but can't figure out if I can implement my own to accomplish this goal.
P.S. The only way I can think to achieve this is to forgo #Valid/#Validated and call Validator#validate(obj, ...) programmatically for the parameter and return value inside the try-catch block, catch the constraint exception, and throw my CustomException(). However, I want to know if I can avoid that and leave this hard coded validation out of the repository.
Since I was not here a year ago I would not have answered but since this is a common question as I can see through my experience with fellow students, I think the answer might be #ControllerAdvice, comes handy with any type of exceptions
doc --> https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/ControllerAdvice.html
I am working on spring4 mvc to introduce in our new web application and currently we are using struts1.x and wanted to use new framework for support html5/ajax request as simple as possible and wanted to use the power of DI and spring webflow support.
Currently in our struts1.x application db transaction management is done at our custom GenericAction which is subclass of Action, In GenericAction we are getting the connection from data source and handover to the subclasses then any exception raised then caught and rollback, otherwise commit such that database atomicity (transaction management )is done at one place. All the module action classes should extends GenericAction such that database connection will be available and performs the module related stuff and after completing connection will be either rollback or commit in GenericAction as said above.
In spring, scope of the Transaction is started with #Transactional annotation and then ends with a method in Service Class since the service class marked as #Transactional. This is not feasible solution for me. I have read several documents related to the spring transaction before starting and below are my questions.
I am using HibernateTransactionManager as transaction manager
Transaction should start from interceptors in case web request or any class (in case of unit testing).
Transaction should ends with the after interceptor is executed in case of web request or any class in case of unit testing.
In case of any exception raised then our HandlerExceptionResolverImpl handler will execute then connection should be rollback.
Any workaround or best practices would be greatly appreciate.
Thanks
Dhorrairaajj
In my opinion, you can achieve this with minimal change on the current application by following what Spring does in its org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(Object, TransactionDefinition). In detail, I think something like following should work:
On the GenericAction, get connection from data source (as you've already done)
Bind the connection via its holder with the current data source:
TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
Eventually, commit the transaction as org.springframework.jdbc.datasource.DataSourceTransactionManager.doCommit(DefaultTransactionStatus).
In any case, Spring's http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/transaction/support/TransactionTemplate.html can help to achieve any programmatic transactional scenario that you want. Just need to ensure that the right transactional resource is bound to the opening transaction.
Hope this helps.
Thanks for you reply. Your post/suggestion drives to do the following
I written an interceptor called TxtManager and below are methods to perform the transaction management.
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception
{
Session session = sessionFactory.openSession();
SessionHolder hold = new SessionHolder(
session);
TransactionSynchronizationManager.bindResource(sessionFactory, hold);
// passing null would accept the default transaction definition.
status = transactionManager.getTransaction(null);
return true;
}
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception
{
Exception hanlderEx = (Exception) request
.getAttribute(HandlerExceptionResolverImpl.EXCEPTION_KEY);
if (hanlderEx != null)
{
transactionManager.rollback(status);
}
else
{
transactionManager.commit(status);
}
}
In HandlerExceptionResolverImpl class which responsible for handling things upon exception, put an exception object in request and read the same exception to rollback or commit in afterCompletion method of the interceptor.
In between(preHandler and afterCompletion) we will going to use the standard practices to perform the module related stuff including any other interceptos. Unit testing may not be supported, i will check other alternative for unit testing.
Finally we were able to simulate the existing frame work with your suggestion Thanks
Any suggestion to improves this would be much appreciate !!..
Thanks
Dhorrairaajj
I have heard that it is possible to log (or do something else) Exceptions with Spring in my web-App, so I don't have to manually insert in every "catch(){}" block the Log-function.
Does anyone have experience with Spring-overall-logging? I just want to get informed when an error appears
ExceptionHandler is the central point for handling unexpected Exceptions that are thrown during the Faces lifecycle. The ExceptionHandler must not be notified of any Exceptions that occur during application startup or shutdown.
See the specification prose document for the requirements for the default implementation. Exceptions may be passed to the ExceptionHandler in one of two ways:
1.)By ensuring that Exceptions are not caught, or are caught and re-thrown.
This approach allows the ExceptionHandler facility specified in section JSF.6.2 to operate on the Exception.
2.)By using the system event facility to publish an ExceptionQueuedEvent that wraps the Exception.
This approach requires manually publishing the ExceptionQueuedEvent, but allows more information about the Exception to be stored in the event. The following code is an example of how to do this.
Global Exception Handler – Exception Handling is a cross-cutting concern, it should be done for all the pointcuts in our application. We have already looked into Spring AOP and that’s why Spring provides #ControllerAdvice annotation that we can use with any class to define our global exception handler.
The handler methods in Global Controller Advice is same as Controller based exception handler methods and used when controller class is not able to handle the exception.
Sample Code
#ExceptionHandler(Exception.class)
public ModelAndView getExceptionPage(Exception e, HttpServletRequest request) {
request.setAttribute("errorMessageObject", e.toString());
return model;
}
** Here we can catch the base exception class Exception.class or any other exception class. Also we can throw and catch our own custom defines exception class.
I develope a registration function. A new user account will be created in the database, and after that a welcome email will be sent. The email sending function can execute a exeption. If a exception will be thrown the created user account has to be deleted. The problems is that my stateless bean rollbacks only if an unchecked exception will be thrown. My exception is a checked exception. Either I have to wrap in a unchecked exception, then I cant response with a proper http status code (using jax rs) or I have to execute a manual rollback.
Can someone help and offer a better solution for my scenario?
Thanks!
You can annotate your checked exception with the annotation: #ApplicationException(rollback = true).
e.g.:
#ApplicationException(rollback = true)
public class MyApplicationException extends Exception{
}
This will mark the current transaction for rollback.
Edit:
I do not want to use a exception to execute a rollback because I cant response with a http status code
Other alternative is to explicitly marks the transaction for rollback using the SessionContext.setRollbackOnly() method. I don't know your app design, but an example would be:
#Stateless
public class MyBean(){
#Resource
private SessionContext ctx;
public void doSomething() {
try {
//code that sends a mail
} catch (Exception e) {
ctx.setRollbackOnly(); //the transaction will not commit
//log exception
}
}
}
I'm currently working with MyBatis-Spring integration framework and that's what I read from docs:
Rather than code data access objects (DAOs) manually using SqlSessionDaoSupport or SqlSessionTemplate,
Mybatis-Spring provides a proxy factory: MapperFactoryBean. This class lets you inject data mapper interfaces
directly into your service beans. When using mappers you simply call them as you have always called your
DAOs, but you won't need to code any DAO implementation because MyBatis-Spring will create a proxy for
you.
That's a very nice feature... but what about exception handling? Where should I translate SQL errors? In my service layer? But wouldn't it violate service-DAO patterns?
Example:
public final class AccountServiceImpl implements AccountService {
(...)
private AccountMapper accountMapper;
(...)
#Override
public void addAccount(Account account) throws AccountServiceException {
//Validating, processing, setting timestamps etc.
(...)
//Persistence:
int rowsAffected;
try {
rowsAffected = accountMapper.insertAccount(account);
} catch (Exception e) {
String msg = e.getMessage();
if (msg.contains("accounts_pkey"))
throw new AccountServiceException("Username already exists!");
if (msg.contains("accounts_email_key"))
throw new AccountServiceException("E-mail already exists!");
throw new AccountServiceException(APP_ERROR);
}
LOG.debug("Rows affected: '{}'", rowsAffected);
if (rowsAffected != 1)
throw new AccountServiceException(APP_ERROR);
}
Is it OK to translate exceptions in service layer?
How should it be done?
Thanks in advance for you advice.
Having recently used mybatis-spring for a project I came across the same stumbling block. I also didn't want to litter my service class with DAO exception handling, particularly since some methods in my service layer required read-only access to a lot of different tables.
The solution I arrived at was to catch the exceptions in the service layer but create your own exception type that takes the caught exception as a parameter. This can then filter out what kind of error message should be contained when the exception is actually constructed and remove the need for string matching (in the service layer at least).
You are close to that there, except the AccountServiceException would have a constructor that took the Exception e as a parameter. I also chose to try and do all my data access as early as possible and wrap it all in a single try/catch. Since the MapperFactoryBean always translates thrown exceptions in to Spring DataAccessExceptions you don't have to worry about catching other kinds of exceptions when doing data access.
I hesitate to consider this an answer as such - more of a sharing of experience given I came across that and hesitated as well.
Translating low level DataAccessExceptions thrown by MyBatis to application-defined ones in service layer is a standard practice.
It's usually connected to transaction handling as you can't handle the transaction spanning multiple DAOs in DA layer.
So yes it's OK and even recommended.
Normally I log the exceptions thrown by DAO in error log and rethrow something defined by application.