Exception applies after catching

I have the strangest thing, and I can’t understand why. The best way to describe this is to provide a simplified example:

@Service @Transactional public class Foo{ public ModelAndView delete(@ModelAttribute("abc") Long id) { ModelAndView mav = new ModelAndView(); try { getDaoService().delete(id); //Calls Bar.delete() } catch (final Exception e) { // Add a custom error message to the mav for the user to see mav.getModelMap().addAttribute(blah, blah); } return mav; } } @Service @Transactional public class Bar { public void delete(final E entity) throws HibernateException { if (null != entity) { try { sessionFactory.getCurrentSession().delete(entity); } finally { sessionFactory.getCurrentSession().flush(); } } } } 

In this particular case, I am trying to delete an object that has a restriction restriction (ORA-02292). Because of this, I expect the deletion to fail. When the deletion failed, I want to show the user the corresponding user message.

Instead of showing the user a custom message, the call fails and displays the following on the screen:

org.springframework.transaction.UnexpectedRollbackException: transaction rollback because it was marked as rollback only

When I use the debugger, I see that the error is appropriately caught and that the ModelAndView object has its own message inside it. Thus, I do not know why the exception is still thrown after it has been caught and considered. Does anyone know why this is happening?

+6
source share
4 answers

In the @Transactional annotation @Transactional you can specify whether the transaction should be rolled back due to this exception using the noRollbackForClassName attribute. You can do it similar to this.

 @Service @Transactional(noRollbackForClassName = "java.lang.Exception") public class YourClass { ... } 

However, note that simply saying noRollbackForClassName = "java.lang.Exception" means that it will not roll back for any Exception (or its subclasses), therefore, this is not good practice.

What you need to do is figure out which exception is actually thrown first (maybe by printing e.getClass().getName() ), and then set this class name to noRollbackForClassName.

Reasonable, this is because if an exception is thrown when you try to delete (), the current transaction is automatically marked as rollback only, and if it is attempted, the exception that you see will be thrown. The data transfer method is to explicitly indicate that this particular exception should not cause a rollback.

+5
source

The problem is that after the exception is thrown, Spring internally marks tx as a rollback. This is completely different from Java exception handling. You have several options:

  • Make sure your expected exception does not raise exceptions that extend the RuntimeException ; Spring only returns tx when its type is RuntimeException ( see this page , section 10.5.3). HibernateException extends RuntimeException , so you get a rollback token.
  • Run each tx in its own transaction by moving the transactional method to its class and annotating it with @Transactional(propagation=Propagation.REQUIRES_NEW) . Then each call will work in its own tx and will not affect the overall tx.
  • Use the venerable style noRollbackForClassName . But use with caution for the indicated reason.
+4
source

The exception is thrown in Bar # delete and gets into Foo # delete. There is an @Transactional annotation on the removal of Bar #, which intersects before an exception is detected. This internal transaction is involved in an external transaction, and therefore the entire transaction is marked for rollback.

To avoid this, you can remove the @Transactional annotation to remove Bar #. This method is already being called as part of another transaction.

+1
source

Add the "globalRollbackOnParticipationFailure" property to the hibernateTransactionManager bean definition as follows.

 <bean id="hibernateTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="hibernateSessionFactory" /> **<property name="globalRollbackOnParticipationFailure" value="false" />** </bean> 
0
source

Source: https://habr.com/ru/post/900042/


All Articles