一言蔽之
對Spring事務(wù)了解一直處于理論階段,幾個事務(wù)傳播行為(propagation behaviors),幾個隔離級別(isolation levels)看了又忘,忘了又看。近期的開發(fā)中遇到了幾個有趣的例子,拿出來曬曬。
具體應(yīng)用
1. 在catch塊中,繼續(xù)進(jìn)行數(shù)據(jù)庫操作
場景
以下是簡化后的Kotlin代碼
execute()在事務(wù)內(nèi)執(zhí)行
doSomeThing()可能會拋出RuntimeException
fail()會對數(shù)據(jù)庫有一些修改操作
@Transactional
override fun execute() {
try {
doSomeThing()
} catch (e: Exception) {
fail()
}
}
override fun fail() {
saveLogToDB()
}
問題
如果doSomeThing()拋出RuntimeException,fail()在執(zhí)行數(shù)據(jù)庫操作時,會拋出異常:
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
分析&思路
- 方法
saveLogToDB()執(zhí)行時事務(wù)已經(jīng)被標(biāo)記為rollback-only -
saveLogToDB()使用的事務(wù)是在execute()方法執(zhí)行之前創(chuàng)建的 - 在
doSomeThing()執(zhí)行的某個步驟中,因為觸發(fā)異常,事務(wù)被標(biāo)記為rollback-only - 那么,為了
saveLogToDB()能夠保存數(shù)據(jù)到數(shù)據(jù)庫,就不能夠使用execute()方法的事務(wù)了。
回憶以下有哪些事務(wù)的傳播行為:
// 如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則創(chuàng)建一個新的事務(wù)。
// @Transactional 注解默認(rèn)采用這個方案
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
// 如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則以非事務(wù)的方式繼續(xù)運行。
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
// 如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則拋出異常。
MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
// 創(chuàng)建一個新的事務(wù),如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
// 以非事務(wù)方式運行,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
// 以非事務(wù)方式運行,如果當(dāng)前存在事務(wù),則拋出異常。
NEVER(TransactionDefinition.PROPAGATION_NEVER),
// 如果當(dāng)前存在事務(wù),則創(chuàng)建一個事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來運行;如果當(dāng)前沒有事務(wù),則該取值等價于REQUIRED
// 并非所有的TransactionManager都能支持這個傳播級別
NESTED(TransactionDefinition.PROPAGATION_NESTED);
作者:whthomas
鏈接:http://www.itdecent.cn/p/e56b440e9eb6
來源:簡書
簡書著作權(quán)歸作者所有,任何形式的轉(zhuǎn)載都請聯(lián)系作者獲得授權(quán)并注明出處。
顯然,這里需要使用REQUIRES_NEW傳播行為。
解決
在fail()方法上加上事務(wù),并讓propagation = REQUIRES_NEW
@Transactional(propagation = Propagation.REQUIRES_NEW)
override fun fail() {
saveLogToDB()
}
注意:同一個類中,方法間調(diào)用,事務(wù)AOP生效的前提條件是使用CGLIB方式實現(xiàn)AOP。