Spring Transaction - noRollbackFor won't commit when exception occurs - java

I want that if i get an E-Mail Exception, to NOT rollback the transaction.
I am using HibernateTransactionManager and
set property name="nestedTransactionAllowed" value="true"
because i have nested transactions.
Also because i call this.getService() i have set
lookup-method name="getService" bean="enrollmentProcessorService" .
This way i should get the Spring Proxy.
But if exceptions occurs, the transaction is still rolledback.
my code looks like this:
#Override
#Transactional(propagation = Propagation.REQUIRED, readOnly = false)
public void processConfirmation() throws SystemException {
//do something
this.getService().processConfirmationData(as400ContractId);
}
#Transactional(propagation = Propagation.REQUIRES_NEW, noRollbackFor = {MailException.class})
public void processConfirmationData(final long as400ContractDataId) throws SystemException {
final AS400ContractData as400ContractData = this.readAS400ContractData(as400ContractDataId, false);
this.populateEnrollmentOptionAnswers(as400ContractData.getContractData());
final PersonalData personalData = this.readPersonalData(as400ContractData.getContractData()
.getEpiphanyPersonalData().getPersonalData().getId(), true);
try {
personalData.setConfirmMailSent(true);
as400ContractData.getContractData().getEpiphanyPersonalData().getPersonalData().setConfirmMailSent(true);
this.personalDataDAO.flush();
this.emailService.sendConfirmationMailToLOI(as400ContractData); //commit if exception is thrown here
} catch (final DataAccessException dae) {
LOGGER.error(CANNOT_UPDATE_PERSONAL_DATA_OBJECT, dae);
throw new SystemException(StringUtils.EMPTY, CANNOT_UPDATE_PERSONAL_DATA_OBJECT, dae);
} catch (final MessagingException e) {
LOGGER.error(CANNOT_SEND_CONFIRMATION_EMAIL, e);
throw new SystemException(StringUtils.EMPTY, CANNOT_SEND_CONFIRMATION_EMAIL, e);
} catch (final MailException e) {
Throwable rootCause = e.getRootCause();
System.out.println("caught");

First, you catch the exception, so there is no way the Spring interceptor can see it and rollback the transaction.
And then, even if you didn't catch it, you've configured the method with **no**RollbackFor = {MailException.class}. This means that you don't want a rollback if this exception is thrown. Use rollbackFor = {MailException.class} instead.

Whenever you get exception, current transaction is rollbacked. No rollback rules, for those times when you do not want a transaction to be marked for rollback when an exception is thrown. Try to remove noRollbackFor annotation.
Also you should rethrow exception. Don't catch MailException.

Yesterday, I encountered the same problem.
When I saw the source :org.springframework.transaction.interceptor.TransactionInterceptor#invoke.
I found where the problem is.
this.emailService.sendConfirmationMailToLOI is another Service, so the TransactionAttribute is new.
you can fix this problem :
#Transactional(propagation = Propagation.REQUIRES_NEW, noRollbackFor = {MailException.class})
In this method(emailService.sendConfirmationMailToLOI*), the configuration above

Related

Throwing another exception inside #ExceptionHandler in spring boot is not working

I'm having a CustomException which I handle with the #ExceptionHandler(CustomException.class)
In one case I'll be getting a InvalidFormatException so from there I need to rethrow to my CustomException. I tried throwing the exception but it's not working as expected
#ExceptionHandler(InvalidFormatException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public Error invalidFormat(InvalidFormatException e) throws CustomException {
if (logicToValidate()) {
throw new CustomException();
} else {
return new Error(BAD_REQUEST.name(), e.getMessage(), ErrorLevel.ERROR);
}
}
#ExceptionHandler(CustomException.class)
public Error customException(CustomException e) {
...
}
After doing some research, I found this thread https://github.com/spring-projects/spring-framework/issues/18299. So it's not possible for now to rethrow the exception in spring boot. Instead we can try alternative strategy, if we have any.

#Transactional rollbackFor not working on jboss wildfly

I have a method annotated with #Transactional(rollbackFor = CustomerRollBackException.class) and it is working as expected when I test it in embebed tomcat.
However, I have it deployed on a jboss wildfly, and the same method is not doing the rollback when it throw the exception..
Do you have any idea if is needed any configuration on jboss?
#Override
#Transactional(rollbackFor = CustomerRollBackException.class)
public void importGenericTable(SheetDTO sheetDTO) throws Exception {
// String tableName, List<Object> rows, UserDTO user
Iterator iterator;
String tableName = sheetDTO.getTableName();
....
try{
..
} catch (ParseException | PersistenceException | SQLGrammarException | ConstraintViolationException e) {
logger.error("importGenericTable. Error " + e);
throw new CustomerRollBackException("E_NPRO_UTIL_IMPORT_0001:" + (countRows + 2));
} catch (CustomerNotFoundException e) {
throw new CustomerRollBackException(e.getMessage());
} catch (Exception e) {
throw new CustomerRollBackException("error desconocido");
}
..
It's entering in the first catch and throwing the CustomerRollBackException and the rollback is not executing.
I think with jboss you should use rollbackOn instead
Solution :
Configure datasource on jboss and use it in the application using
spring.datasource.jndi-name=java:/XXXX
instead of :
spring.datasource.url= jdbc:
spring.datasource.username=
spring.datasource.password=

Where should I throw custom exceptions and catch Exception object

I am coding a Spring MVC, hibernate app. I can't decide where should I throw custom exceptions, what custom exceptions to throw and where to catch "Exception", in controller or service or DAO?
I tried to throw a custom exception in controller and service and catch "Exception" in the controller as the last catch block. But every time a custom exception is thrown, the last catch block( of Exception) catches it and throws CustomGenericException overriding the previous one.
//Controller
#PostMapping("/add/{user_id}/{book_id}")
public #ResponseBody
String addToCart(#PathVariable("user_id") Integer user_id,
#PathVariable("book_id") Integer book_id){
try {
return cartService.addBook(user_id, book_id);
}
catch (HibernateException | CannotCreateTransactionException dbException) {
throw new DatabaseDownException("Database error. Could not connect at this time.");
}
catch (Exception ex){
throw new CustomGenericException("Could not add book to cart at this time. Please try again later.");
}
}
//Service
#Override
public String addBook(int user_id, int book_id) {
if(bookDAO.getCount(book_id)>0) {
Cart cart = new Cart(user_id, book_id);
List<Cart> userCarts = cartDAO.getCart(user_id, 0);
for (Cart c : userCarts) {
if (c.getBook_id() == book_id) {
return "Book already in cart!";
}
}
List<Cart> issuedBooks =cartDAO.getCart(user_id, 1);
for(Cart c:issuedBooks){
if(c.getBook_id()==book_id){
return "Book already issued. Can't add another to cart.";
}
}
return cartDAO.addBookToCart(cart);
}
return "No more copies left. Please try again later.";
}
I want to know where should I throw exceptions, where to catch them and how to avoid custom thrown exceptions getting caught by last catch block.
#aks If you want to throw checked exceptions than you should pass "cause" exception to constructor of exception on higher level (in this case the last one catch) because otherwide the cause information is lost. Core Exception class has constructor with cause parameter to pass.
You can also try to use Controller Advice https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
I hope I helped a little bit :)
read about chained exception in Java here
you are not wrapping exception object in your custom exception , that is why you feel like previous exception is getting overridden .

Why JPA/Hibernate transaction is active even when I did not start one explicitly

My problem is that JPA/Hibernate returns true for a call of entityManager.getTransaction().isActive() even when I did not explicitly start a transaction (see code below).
The problem here is that I want to read something from the database and a SerializationException is ok in this scenario, because this just indicates that the persisted object does not fit to the actual code any more and needs to be recalculated. Instead of just returning null the code below throws the following exception:
Transaction rollback failed.
org.hibernate.TransactionException: Unable to rollback against JDBC Connection
This shows me, there must have been a transaction somewhere which I did not start in my code. The finally block in the code above is
final EntityManager entityManager = Persistence.createEntityManagerFactory("test").createEntityManager();
try {
final TypedQuery<Test> query = entityManager.createQuery("SELECT t FROM test t", Test.class);
return query.getResultList();
} catch (final PersistenceException e) {
if (e.getCause() instanceof SerializationException) {
LOG.debug("Implementation changed meanwhile. That's ok - we return null.");
return null;
}
throw e;
} finally {
EntityManagerCloser.closeEntityManager(entityManager);
}
And the EntityManagerCloser looks like this:
public final class EntityManagerCloser {
private static final Logger LOG = LoggerFactory.getLogger(EntityManagerCloser.class);
public static void closeEntityManager(EntityManager entityManager) {
if (entityManager.getTransaction().isActive()) {
try {
entityManager.getTransaction().rollback();
} catch (PersistenceException | IllegalStateException e) {
LOG.error("Transaction rollback failed.", e);
}
}
if (entityManager.isOpen()) {
try {
entityManager.close();
} catch (IllegalStateException e) {
LOG.error("Closing entity manager failed.", e);
}
}
}
}
Hibernate docs says "Always use clear transaction boundaries, even for read-only operations". So do I really need to insert a
entityManager.getTransaction().begin();
....
<do read here>
....
entityManager.getTransaction().commit();
around every read operation I perform on the database?
I could implement another closeEntityManager method for read-only operations without the rollback transaction block but I want to understand why there IS a transaction at all. Thanks for any help!
The problem is that when you call entityManager.getTransaction(); a new transaction object will be created. So it is better to save the transaction reference to a variable as shown below.
Transaction txn = entityManager.getTransaction();
if (txn.isActive()) {
try {
txn.rollback();
} catch (PersistenceException | IllegalStateException e) {
LOG.error("Transaction rollback failed.", e);
}
}
Thanks to Jobin I quickly found the solution to my problem:
I think I need to call entityManager.isJoinedToTransaction() in my closeEntityManager method before calling entityManager.getTransaction().isActive().
This will prevent the EntityManagerCloser to start its own transaction which I can not rollback later because I did not explicitly call transaction.begin() on it.

How can you tell if a database is down or unavailable?

I am using a Spring DefaultMessageListenerContainer to consume messages from a queue. The messages are then saved to an Oracle database.
When the database goes down, I throw an exception out of the onMessage method and that leaves the message on the queue to be reprocessed. Below you can see that on a DataAccessResourceFailureException and CannotCreateTransactionException exception, I throw the exception out of the method, which puts it back on the queue. The other exceptions do not save the message; they correspond to data problems and such.
public void onMessage(javax.jms.Message mqMessage) {
...get the message blah, blah, blah
try {
this.theService.doMessage(tmaticMessage, theHandler);
} catch (DataAccessResourceFailureException e) {
this.slowDown(mqMessage);
throw e;
} catch (CannotCreateTransactionException e) {
this.slowDown(mqMessage);
throw e;
} catch (DataAccessException e) {
...
} catch (TmUnusableMessageException e) {
...
} catch (Exception e) {
...
}
}
Reading the Spring docs, I discovered that DataAccessResourceFailureException should be thrown "... when a resource fails completely: for example, if we can't connect to a database using JDBC." The problem is that I just did a test where I had the DBA take the database down and got a new exception: CannotCreateTransactionException. So that is one more exception that can be thrown. I am wondering if there are others.
I am using Spring Connections and getHibernateTemplate() to make my calls. Here is the question. How do I know what exceptions can be thrown when a database goes down?
Maybe the complexity is caused by various way you can 'take a database down'. For example:
deleting a table
deleting entire database
disabling a db user account
shutting down the database server
All can be considered as 'taking a database down', but each could cause a different exception being thrown
If you browse through following sections in spring javadoc, there are lists of exceptions that could be thrown:
http://static.springsource.org/spring/docs/2.0.x/api/org/springframework/dao/package-frame.html
http://static.springsource.org/spring/docs/2.0.x/api/org/springframework/transaction/package-frame.html

Categories