How To Rollback Spring Transaction When Throwing Exceptions

I am using spring 3.0.5 and sleep mode 3.6. There is a scenario in my project when I have to roll back a transaction of any of the exceptions that occur or an error occurs. This is sample code, everything works fine, except that the transaction does not return when I throw an exception, but if any exception is thrown, for example mysql.IntegrityConstraintException , then the transaction rolls back, why doesn’t this happen in my case?

applicationContext.xml

<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="classpath:database.properties"/> </bean> <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="dataSource" ref="myDataSource" /> <property name="packagesToScan" value="com.alw.imps"/> <property name="configLocation"> <value> classpath:hibernate.cfg.xml </value> </property> </bean> <bean id="stateDao" class="com.alw.imps.dao.StateDaoImpl"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <bean id="stateService" class="com.alw.imps.services.StateService"> <property name="stateDao" ref="stateDao"></property> <property name="cityDao" ref="cityDao"></property> <property name="customerDao" ref="customerDao"></property> </bean> <bean id="customerDao" class="com.alw.imps.dao.CustomerDaoImpl"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <bean id="cityDao" class="com.alw.imps.dao.CityDaoImpl"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <tx:annotation-driven transaction-manager="transactionManager" /> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <tx:advice id = "txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED" /> </tx:attributes> </tx:advice> 

Service class StateService

 @Transactional(rollbackFor={Exception.class}) public class StateService { private StateDaoImpl stateDao; private CityDao cityDao; private CustomerDao customerDao; public void setCustomerDao(CustomerDao customerDao) { this.customerDao = customerDao; } public void setStateDao(StateDaoImpl stateDao) { this.stateDao = stateDao; } public CityDao getCityDao() { return cityDao; } public void setCityDao(CityDao cityDao) { this.cityDao = cityDao; } public void addState() { try { State state=new State(); state.setStateName("Delhi"); stateDao.create(state); addCity(); addCustomer(); } catch(Exception e) { e.printStackTrace(); } } public void addCity() throws Exception { City city=new City(); city.setCiytName("Delhi"); city.setStateId(1); cityDao.create(city); } public void addCustomer() throws Exception { throw new java.lang.Exception(); } 

DAO

 public class StateDaoImpl extends GenericDaoImpl<State, Integer> implements StateDao { } 

GenericDaoImpl

 public class GenericDaoImpl<T,PK extends Serializable> implements GenericDao<T,PK> { public SessionFactory sessionFactory; public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } public Session getSession() { return sessionFactory.getCurrentSession(); } public PK create(T o) { Session ss= getSession(); ss.save(o); return null; } 

hibernate.cfg

 <hibernate-configuration> <session-factory> <property name="connection.pool_size">1</property> <property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property> <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property> <property name="show_sql">true</property> <property name="hbm2ddl.auto">update</property> <property name="defaultAutoCommit">false</property> <mapping class="com.alw.imps.pojo.State"/> <mapping class="com.alw.imps.pojo.City"/> </session-factory> </hibernate-configuration> 

Since I said that my problem is that the transaction is not getting rolled back when I selected an exception of type Exception from the addCustomer () method

+4
source share
4 answers

Your transaction will not be rolled back because an exception does not occur: the addState() method that you call throws an exception:

 public void addState() { try { State state=new State(); state.setStateName("Delhi"); stateDao.create(state); addCity(); addCustomer(); } catch(Exception e) { e.printStackTrace(); } } 

Thus, the Spring transactional proxy does not see any exceptions thrown and does not roll back the transaction.

It works for exceptions thrown from the DAO because the DAO itself is transactional, so its own transactional proxy detects the exception thrown by the DAO and marks the transaction for rollback. Then the exception extends to the service and is delayed by your code, but the transaction is already marked for rollback at this point.

+12
source

Your transaction will not be rolled back, because you do not allow Exception to reach the Spring framework, you catch the exception in the code itself. Therefore, instead of

 public void addState() { try { State state=new State(); state.setStateName("Delhi"); stateDao.create(state); addCity(); addCustomer(); } catch(Exception e) { e.printStackTrace(); } } 

using

 public void addState() { State state=new State(); state.setStateName("Delhi"); stateDao.create(state); addCity(); addCustomer(); } 
+4
source

The transaction was not discarded, because you yourself will catch this exception by writing a catch block.

This can be done in normal cases, but in a spring transaction, if you do, then the spring transaction manager knows that an exception is occurring. That is why it does not roll back.

+1
source

You can find answers to most questions in the Spring API doc. @Transactional has a Class<? extends Throwable>[] rollbackFor() Class<? extends Throwable>[] rollbackFor() :

By default, the transaction will be rolled back to RuntimeException and Error , but not to checked exceptions (business exceptions). org.springframework.transaction.interceptor.DefaultTransactionAttribute.rollbackOn(Throwable) explain org.springframework.transaction.interceptor.DefaultTransactionAttribute.rollbackOn(Throwable) .

This means that in the following cases, no matter how the caller deals with exceptions, only the first case of RuntimeException will call the role function by default.

 // Only this case would roll back by default @Override @Transactional public void testRollbackRuntimeException() { // jpa operation. throw new RuntimeException("test exception"); } // never roll back, because its caught. @Override @Transactional public void testRollbackRuntimeExceptionCaught() { try { throw new RuntimeException("test exception"); } catch(Exception e) {} } // @Transactional(rollbackFor = Exception.class) would also rollback. but by default no @Override @Transactional public void testRollBackWithExceptionCaught() throws Exception { throw new Exception("test exception"); } // never roll back because the checked exception is caught. @Override @Transactional public void testRollBackWithExceptionCaught() { try { throw new Exception("test exception"); } catch (Exception e) {} } 

And basically you can discard checked exceptions, @Transactional(rollbackFor = Exception.class)

0
source

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


All Articles