简体   繁体   中英

How to rollback Spring Transaction when an Exception is thrown

I am using spring 3.0.5 and hibernate 3.6. In my project there is a scenario where i have to rollback transaction of any exception in thrown or error occurs. This the sample code, Everything works fine except transaction is not getting rolled back when I throw an Exception but if any exception is thrown such as mysql.IntegrityConstraintException then transaction gets rolled back, why this is not happening 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>

So as I said my problem is transaction is not getting rollback when i throw an exception of type Exception from method addCustomer()

Your transaction is not rollbacked because there is no exception thrown: the addState() method that you call catches the exception:

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

So the transactional Spring proxy doesn't see any exception thrown and doesn't rollback the transaction.

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

Your transaction is not getting rolled back because you are not letting Exception to reach to Spring framework, you are catching the exception in your code itself. So instead of

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

            e.printStackTrace();
        }
}

use

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

The transaction has not been rolled back, because you are catching the exception yourself, by writing catch block..

This can be done in normal cases, but in spring transaction, if you do so, how does spring transaction manager knows the exception is happening.. thats why it is not getting rolled back.

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

By default, a transaction will be rolling back on RuntimeException and Error but not on checked exceptions (business exceptions).See org.springframework.transaction.interceptor.DefaultTransactionAttribute.rollbackOn(Throwable) for a detailed explanation.

Meaning that, for the following cases, no matter how the caller deal with exceptions, only the first RunTimeException case would call roleback 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 mostly you may want to roll back for checked exceptions undistinguishedly, using @Transactional(rollbackFor = Exception.class)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM