上一篇:事務(wù)的兩種形式
@Transactional介紹
@Transactional注解底層使用的是動(dòng)態(tài)代理來(lái)進(jìn)行實(shí)現(xiàn)的
Transactional注解可以作用在接口、類、類方法
- 作用接口: 不推薦,因?yàn)檫@只有在使用基于接口的代理時(shí)它才會(huì)生效
- 作用于類:表示該類的所有public方法都配置相同的事務(wù)屬性信息
- 作用于類方法:只能用于public方法上。注意:如果類于類方法都配置了@Transactional,類方法事務(wù)會(huì)覆蓋類事務(wù)
propagation屬性
propagation代表事務(wù)的傳播行為,默認(rèn)值為Propagation.REQUIRED
- Propagation.REQUIRED:如果當(dāng)前存在事務(wù),則加入該事務(wù),如果當(dāng)前不存在事務(wù),則新創(chuàng)建事務(wù)(也就是說(shuō)如果A方法和B方法都添加了注解,默認(rèn)傳播模式下,A方法調(diào)用B方法,會(huì)將兩個(gè)方法事務(wù)合并為一個(gè))
- Propagation.SUPPORTS:如果當(dāng)前存在事務(wù),則加入,如果不存在,則以非事務(wù)形式運(yùn)行
- Propagation.MANDATORY:如果當(dāng)前存在事務(wù),則加入事務(wù),如果不存在事務(wù),則拋異常
- Propagation.REQUIRES_NEW:重新創(chuàng)建一個(gè)事務(wù),如果當(dāng)前存在事務(wù),暫停當(dāng)前事務(wù)(如果A方法默認(rèn)為Propagation.REQUIRED模式,B方法為Propagation.REQUIRES_NEW,在A方法中調(diào)用B方法,A方法拋出異常后,B方法不會(huì)回滾,因?yàn)镻ropagation.REQUIRES_NEW會(huì)暫停A方法的事務(wù))
- Propagation.NOT_SUPPORTED:以非事務(wù)方法運(yùn)行,如果當(dāng)前存在事務(wù),暫停當(dāng)前事務(wù)
- Propagation.NESTED :和 Propagation.REQUIRED 效果一樣
isolation屬性
isolation事務(wù)的隔離級(jí)別,默認(rèn)值為Isolation.DEFAULT
- Isolation.DEFAULT:使用底層數(shù)據(jù)庫(kù)默認(rèn)的隔離級(jí)別
- Isolation.READ_UNCOMMITTED
- Isolation.READ_COMMITTED
- Isolation.REPEATABLE_READ
- Isolation.SERIALIZABLE
timeout屬性
timeout事務(wù)的超時(shí)時(shí)間,默認(rèn)值:-1,如果超過該時(shí)間限制事務(wù)還未完成,則自動(dòng)回滾
readOnly屬性
readOnly指定事務(wù)是否為只讀事務(wù),默認(rèn)值為false,它的存在是為了讓那些不需要事務(wù)的方法被排除掉,例如:讀取數(shù)據(jù),可以設(shè)置為readOnly為true
rollbackFor屬性
用于指定能夠觸發(fā)事務(wù)回滾的異常類型,可以指定多個(gè)異常類型
noRollbackFor屬性
指定的異常類型,不回滾事務(wù),可以指定多個(gè)異常類型
@Transactional失效場(chǎng)景
-
@Transactional注解應(yīng)用在非public方法上
image.png
之所以會(huì)失效式因?yàn)樵赟pring AOP代理時(shí),如上圖所示 TransactionInterceptor (事務(wù)攔截器)在目標(biāo)方法執(zhí)行前后進(jìn)行攔截,DynamicAdvisedInterceptor(CglibAopProxy 的內(nèi)部類)的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法會(huì)間接調(diào)用 AbstractFallbackTransactionAttributeSource的 computeTransactionAttribute 方法,獲取Transactional 注解的事務(wù)配置信息。
image.png propagation 配置錯(cuò)誤
其中這三種會(huì)存在以非事務(wù)形式運(yùn)行
TransactionDefinition.PROPAGATION_SUPPORTS、TransactionDefinition.PROPAGATION_NOT_SUPPORTED、
TransactionDefinition.PROPAGATION_NEVER;
Propagation.REQUIRES_NEW也會(huì)存在事務(wù)失效的情況,見上述Propagation.REQUIRES_NEW的舉例描述-
rollbackFor配置錯(cuò)誤
image.png
Spring默認(rèn)拋出了未檢查unchecked異常(繼承自 RuntimeException 的異常)或者 Error才回滾事務(wù);其他異常不會(huì)觸發(fā)回滾事務(wù)。如果在事務(wù)中拋出其他類型的異常,但卻期望 Spring 能夠回滾事務(wù),就需要指定 rollbackFor屬性。
希望自定義的異??梢赃M(jìn)行回滾@Transactional(propagation= Propagation.REQUIRED,rollbackFor= MyException.class
若在目標(biāo)方法中拋出的異常是 rollbackFor 指定的異常的子類,事務(wù)同樣會(huì)回滾。
- 同一個(gè)類中方法間的調(diào)用
A調(diào)用本類中的B方法(不論B時(shí)public或者private),A沒有聲明事務(wù),而B方法有。外部調(diào)用A方法后,方法B的事務(wù)不會(huì)起作用。
這是由于使用Spring AOP代理造成的,因?yàn)橹挥挟?dāng)前事務(wù)方法被當(dāng)前類以外的代碼調(diào)用時(shí),才會(huì)由Spring生成的代理對(duì)象來(lái)管理。
//@Transactional
@GetMapping("/test")
private Integer A() throws Exception {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setCityName("2");
/** * B 插入字段為 3的數(shù)據(jù) */
this.insertB();
/** * A 插入字段為 2的數(shù)據(jù) */
int insert = cityInfoDictMapper.insert(cityInfoDict);
return insert;
}
@Transactional()
public Integer insertB() throws Exception {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setCityName("3");
cityInfoDict.setParentCityId(3);
return cityInfoDictMapper.insert(cityInfoDict);
}
- 異常被你的catch“吃了”
@Transactional
private Integer A() throws Exception {
int insert = 0;
try {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setCityName("2");
cityInfoDict.setParentCityId(2);
/** * A 插入字段為 2的數(shù)據(jù) */
insert = cityInfoDictMapper.insert(cityInfoDict);
/** * B 插入字段為 3的數(shù)據(jù) */
b.insertB();
} catch (Exception e) {
e.printStackTrace();
}
}
如果B方法發(fā)生了異常,而A方法catch住了B方法的異常,那么這個(gè)事務(wù)不能正?;貪L
會(huì)拋出:org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
因?yàn)楫?dāng)B方法中拋出了一個(gè)異常后,B標(biāo)識(shí)當(dāng)前事務(wù)需要rollback。但是A中由于收到鋪貨了這個(gè)異常并進(jìn)行了處理,A任務(wù)當(dāng)前事務(wù)應(yīng)該可以正常commit。此時(shí)前后不一致,拋出UnexpectedRollbackException
Spring事務(wù)時(shí)在調(diào)用業(yè)務(wù)方法之前開始的,業(yè)務(wù)方法執(zhí)行完畢之后才執(zhí)行commit or rollback,事務(wù)是否執(zhí)行取決于是否拋出RuntimeException,如果拋出了并且未catch到的話,事務(wù)就會(huì)回滾。
- 數(shù)據(jù)庫(kù)不支持事務(wù)
mysql數(shù)據(jù)庫(kù)默認(rèn)使用支持事務(wù)的innodb引擎,一旦切換為不支持事務(wù)的myisam,那么事務(wù)就會(huì)失效
原文鏈接:https://baijiahao.baidu.com/s?id=1661650900351466294&wfr=spider&for=pc


