Java 抛出异常时如何回滚Spring事务
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16421508/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to rollback Spring Transaction when an Exception is thrown
提问by arvin_codeHunk
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.IntegrityConstraintExceptionthen transaction gets rolled back, why this is not happening in my case?
我正在使用 spring 3.0.5 和 hibernate 3.6。在我的项目中有一个场景,我必须回滚抛出或发生错误的任何异常的事务。这是示例代码,除了当我抛出异常时事务没有回滚外,一切正常,但是如果抛出任何异常,例如mysql.IntegrityConstraintException然后事务被回滚,为什么在我的情况下没有发生这种情况?
applicationContext.xml
应用上下文.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
服务类 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
通用DaoImpl
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
休眠.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 Exceptionfrom method addCustomer()
所以正如我所说,我的问题是当我从方法addCustomer()抛出类型为Exception的异常时,事务没有得到回滚
采纳答案by JB Nizet
Your transaction is not rollbacked because there is no exception thrown: the addState()
method that you call catches the exception:
您的事务没有回滚,因为没有抛出异常:addState()
您调用的方法捕获了异常:
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.
因此事务性 Spring 代理不会看到任何抛出的异常,也不会回滚事务。
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.
它适用于从 DAO 抛出的异常,因为 DAO 本身是事务性的,所以它自己的事务代理检测到 DAO 抛出的异常并将事务标记为回滚。然后异常会传播到服务并由您的代码捕获,但此时事务已标记为回滚。
回答by Sachin Gorade
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
您的事务没有回滚,因为您没有让 Exception 到达 Spring 框架,而是在代码本身中捕获异常。所以代替
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();
}
回答by user2929
The transaction has not been rolled back, because you are catching the exception yourself, by writing catch block..
事务尚未回滚,因为您自己通过编写 catch 块来捕获异常。
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.
这可以在正常情况下完成,但在 spring 事务中,如果你这样做,spring 事务管理器如何知道异常正在发生......这就是它没有被回滚的原因。
回答by Tiina
You can find answers to most questions in Spring API doc.
@Transactional
has a field Class<? extends Throwable>[] rollbackFor()
:
您可以在 Spring API 文档中找到大多数问题的答案。
@Transactional
有一个字段Class<? extends Throwable>[] rollbackFor()
:
By default, a transaction will be rolling back on
RuntimeException
andError
but not on checked exceptions (business exceptions).Seeorg.springframework.transaction.interceptor.DefaultTransactionAttribute.rollbackOn(Throwable)
for a detailed explanation.
默认情况下,事务将回滚
RuntimeException
,Error
但不会回滚已检查的异常(业务异常)org.springframework.transaction.interceptor.DefaultTransactionAttribute.rollbackOn(Throwable)
。有关详细说明,请参阅。
Meaning that, for the following cases, no matter how the caller deal with exceptions, only the first RunTimeException
case would call roleback by default.
这意味着,对于以下情况,无论调用者如何处理异常,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) {}
}
And mostly you may want to roll back for checked exceptions undistinguishedly, using
@Transactional(rollbackFor = Exception.class)
大多数情况下,您可能想要毫无区别地回滚已检查的异常,使用
@Transactional(rollbackFor = Exception.class)