先說結(jié)論:Spring事務(wù)異成杌回滾疯潭,捕獲異常不拋出就不會回滾。
最近遇到了事務(wù)不回滾的情況祝旷,我還考慮說JPA的事務(wù)有bug履澳? 我想多了.......
為了打印清楚日志,很多方法我都加tyr catch怀跛,在catch中打印日志奇昙。但是這邊情況來了,當(dāng)這個方法異常時候 日志是打印了敌完,但是加的事務(wù)卻沒有回滾储耐。
例:
類似這樣的方法不會回滾 (一個方法出錯,另一個方法不會回滾) :
if(userSave){
try {
userDao.save(user);
userCapabilityQuotaDao.save(capabilityQuota);
} catch (Exception e) {
logger.info("能力開通接口滨溉,開戶異常什湘,異常信息:"+e);
}
}
下面的方法回滾(一個方法出錯,另一個方法會回滾):
if(userSave){
try {
userDao.save(user);
userCapabilityQuotaDao.save(capabilityQuota);
} catch (Exception e) {
logger.info("能力開通接口晦攒,開戶異常闽撤,異常信息:"+e);
throw new RuntimeException();
}
}
或者:
if(userSave){
try {
userDao.save(user);
userCapabilityQuotaDao.save(capabilityQuota);
} catch (Exception e) {
logger.info("能力開通接口,開戶異常脯颜,異常信息:"+e);
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
為什么不會滾呢哟旗??是對Spring的事務(wù)機制就不明白。U⒉汀饱亮!
默認(rèn)spring事務(wù)只在發(fā)生未被捕獲的 runtimeexcetpion時才回滾。
spring aop 異常捕獲原理:被攔截的方法需顯式拋出異常舍沙,并不能經(jīng)任何處理近上,這樣aop代理才能捕獲到方法的異常,才能進行回滾拂铡,默認(rèn)情況下aop只捕獲runtimeexception的異常壹无,但可以通過 。
配置來捕獲特定的異常并回滾 ;
換句話說在service的方法中不使用try catch 或者在catch中最后加上throw new runtimeexcetpion()感帅,這樣程序異常時才能被aop捕獲進而回滾.
解決方案:
方案1.例如service層處理事務(wù)斗锭,那么service中的方法中不做異常捕獲,或者在catch語句中最后增加throw new RuntimeException()語句失球,以便讓aop捕獲異常再去回滾拒迅,并且在service上層(webservice客戶端,view層action)要繼續(xù)捕獲這個異常并處理.
方案2.在service層方法的catch語句中增加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();語句她倘,手動回滾璧微,這樣上層就無需去處理異常(現(xiàn)在項目的做法)
阿里編碼規(guī)約示例:
Severity
Major
Message
事務(wù)場景中,拋出異常被catch后硬梁,如果需要回滾前硫,一定要手動回滾事務(wù)。
Examples
Positive example 1:
/**
* @author caikang
* @date 2017/04/07
*/
@Service
@Transactional(rollbackFor = Exception.class)
public class UserServiceImpl implements UserService {
@Override
public void save(User user) {
//some code
//db operation
}
}
Positive example 2:
/**
* @author caikang
* @date 2017/04/07
*/
@Service
public class UserServiceImpl implements UserService {
@Override
@Transactional(rollbackFor = Exception.class)
public void save(User user) {
//some code
//db operation
}
}
Positive example 3:
/**
* @author caikang
* @date 2017/04/07
*/
@Service
public class UserServiceImpl implements UserService {
@Autowired
private DataSourceTransactionManager transactionManager;
@Override
@Transactional
public void save(User user) {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// explicitly setting the transaction name is something that can only be done programmatically
def.setName("SomeTxName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = transactionManager.getTransaction(def);
try {
// execute your business logic here
//db operation
} catch (Exception ex) {
transactionManager.rollback(status);
throw ex;
}
}
}