6

我正在使用弹簧 3.0.5 和休眠 3.6。在我的项目中有一个场景,我必须回滚任何抛出异常或发生错误的事务。这是示例代码,一切正常,除了当我抛出异常时事务没有回滚但是如果抛出任何异常,例如mysql.IntegrityConstraintException 然后事务被回滚,为什么在我的情况下没有发生这种情况?

应用程序上下文.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>

服务类 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();
  }

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;
  }

休眠.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>

所以正如我所说,我的问题是当我从方法addCustomer()抛出异常类型的异常时事务没有回滚

4

4 回答 4

15

您的事务没有回滚,因为没有抛出异常:addState()您调用的方法捕获了异常:

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

所以事务 Spring 代理看不到任何抛出的异常,也不会回滚事务。

它适用于从 DAO 抛出的异常,因为 DAO 本身是事务性的,因此它自己的事务代理检测到 DAO 抛出的异常并将事务标记为回滚。然后将异常传播到服务并由您的代码捕获,但此时事务已标记为回滚。

于 2013-05-08T06:55:18.213 回答
5

您的事务没有回滚,因为您没有让 Exception 到达 Spring 框架,而是在代码本身中捕获了异常。所以而不是

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

            e.printStackTrace();
        }
}

采用

public void addState() 
{
        State state=new State();
        state.setStateName("Delhi");
        stateDao.create(state);
        addCity();
        addCustomer();
}
于 2013-05-08T06:57:27.457 回答
1

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.

于 2016-07-01T13:49:53.163 回答
0

You can find answers to most questions in Spring API doc. @Transactional has a field 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)

于 2017-10-16T02:29:01.983 回答