mysql的引擎常用的有兩個,一個MyISAM,另一個是InnoDB,mysql默認的為MyISAM,而InnoDB才是支持事務的。所以一般需要修改下,如何修改就不說了。
事務需要依賴數據庫,好久沒使用聲明式事務,今天試了下。關鍵配置如下。
<tx:advice id="transactionAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="add*" propagation="REQUIRED" /> <tx:method name="append*" propagation="REQUIRED" /> <tx:method name="insert*" propagation="REQUIRED" /> <tx:method name="save*" propagation="REQUIRED" /> <tx:method name="update*" propagation="REQUIRED" /> <tx:method name="modify*" propagation="REQUIRED" /> <tx:method name="edit*" propagation="REQUIRED" /> <tx:method name="delete*" propagation="REQUIRED" /> <tx:method name="remove*" propagation="REQUIRED" /> <tx:method name="tx*" propagation="REQUIRED" /> <tx:method name="repair" propagation="REQUIRED" /> <tx:method name="delAndRepair" propagation="REQUIRED" /> <tx:method name="get*" propagation="SUPPORTS" /> <tx:method name="find*" propagation="SUPPORTS" /> <tx:method name="load*" propagation="SUPPORTS" /> <tx:method name="search*" propagation="SUPPORTS" /> <tx:method name="datagrid*" propagation="SUPPORTS" /> <tx:method name="*" propagation="SUPPORTS" /> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="transactionPointcut" expression="execution(* com.wondersgroup.employeeBenefits.*.*.service..*Impl.*(..))" /> <!-- com.wondersgroup.benefit.*.core.service..*Impl.*(..) --> <aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice" /> </aop:config>
事務配置好之后再service中手動拋了個exception,結果沒有回滾,service方法如下
@Override
public int saveBudgetApplyInfo(BudgetApplyInfo budgetApplyInfo) throws Exception {
budgetApplyInfoMapper.insert(budgetApplyInfo);
if(1==1){
throw new Exception();
}
return 1;
}
跟着斷點一步步進去查看原因
在
TransactionAspectSupport中發現這樣一個方法
/**
* Handle a throwable, completing the transaction.
* We may commit or roll back, depending on the configuration.
* @param txInfo information about the current transaction
* @param ex throwable encountered
*/
protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.hasTransaction()) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
"] after exception: " + ex);
}
if (txInfo.transactionAttribute.rollbackOn(ex)) {
try {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
catch (Error err) {
logger.error("Application exception overridden by rollback error", ex);
throw err;
}
}
else {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by commit exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException ex2) {
logger.error("Application exception overridden by commit exception", ex);
throw ex2;
}
catch (Error err) {
logger.error("Application exception overridden by commit error", ex);
throw err;
}
}
}
}
上面的方法中有這么一段
txInfo.transactionAttribute.rollbackOn(ex),這里是判斷是否需要執行回滾操作的,跟蹤rollbackOn方法最后會執行到
DefaultTransactionAttribute中的rollbackOn方法
/**
* The default behavior is as with EJB: rollback on unchecked exception.
* Additionally attempt to rollback on Error.
* <p>This is consistent with TransactionTemplate's default behavior.
*/
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);
}
看到這里,應該都清楚了。。。自己主動拋異常Exception是不對的。這里只捕獲運行時異常
RuntimeException 及Error,所以我們測試時不可以直接拋Exception,而應該換成
RuntimeException 。當然。也可在xml中指定rollback-for
<tx:method name="add*" propagation="REQUIRED" rollback-for="Exception" />
最后:個人還是比較喜歡基於注解的事務處理
