【问题标题】:How to rollback Spring Transaction when an Exception is thrown抛出异常时如何回滚Spring Transaction
【发布时间】:2013-05-01 13:14:59
【问题描述】:

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

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>

服务类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>

正如我所说,我的问题是当我从方法 addCustomer()

抛出 Exception 类型的异常时,事务没有回滚

【问题讨论】:

  • 到目前为止一切看起来都很好,它应该回滚事务。事务是否有可能在调用堆栈的更高位置开始。也许如果您在配置事务的位置添加 spring 配置会有所帮助。
  • 我不相信您的 MyService 课程会被代理。您看到的回滚来自数据库,而不是来自事务。
  • 请发布您的配置和调用堆栈以及方法签名/注释,以便我们更好地评估情况。
  • @Moles-JWS 我已经添加了配置和其他依赖项,请检查更新
  • 你从服务外部调用什么方法? addCustomer() 除了抛出异常之外什么都不做。实际上,你调用 addState() 吗?

标签: java spring hibernate exception spring-transactions


【解决方案1】:

您可以在 Spring API 文档中找到大多数问题的答案。 @Transactional 有一个字段Class&lt;? extends Throwable&gt;[] rollbackFor()

默认情况下,事务将在RuntimeExceptionError 但不适用于已检查异常(业务异常)。有关详细说明,请参阅 org.springframework.transaction.interceptor.DefaultTransactionAttribute.rollbackOn(Throwable)

也就是说,对于以下情况,无论调用者如何处理异常,默认只有第一个RunTimeException情况会调用roleback。

// 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) {}
}

而且大多数情况下,您可能希望不加区分地回滚检查的异常,使用 @Transactional(rollbackFor = Exception.class)

【讨论】:

    【解决方案2】:

    事务还没有回滚,因为你自己在捕捉异常,通过写catch block..

    这在正常情况下可以做到,但在spring事务中,如果你这样做了,spring事务管理器如何知道异常正在发生..这就是它没有回滚的原因。

    【讨论】:

      【解决方案3】:

      您的事务没有回滚,因为您没有让异常到达 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();
      }
      

      【讨论】:

        【解决方案4】:

        您的事务没有回滚,因为没有抛出异常:您调用的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 抛出的异常并将事务标记为回滚。然后将异常传播到服务并被您的代码捕获,但此时事务已标记为回滚。

        【讨论】:

        • 感谢 jb,这是我的错……这是一个愚蠢的错误,不能接受。
        猜你喜欢
        • 2022-07-22
        • 1970-01-01
        • 2014-09-16
        • 1970-01-01
        • 1970-01-01
        • 2020-09-24
        • 1970-01-01
        • 2015-10-03
        • 1970-01-01
        相关资源
        最近更新 更多