解讀@Transactional失效的幾種情況
Spring事務(wù)管理方式
- 編碼式事務(wù)管理:將事務(wù)控制代碼編寫在業(yè)務(wù)代碼之中。
- 聲明式事務(wù)管理:基于AOP(面向切面編程),事務(wù)管理與業(yè)務(wù)邏輯解耦。
聲明式事務(wù)管理的兩種實現(xiàn):
- (1)在配置文件(xml)中配置。
- (2)基于@Transactional注解。
@Transactional使用起來方便,但也需要注意引起@Transactional失效的場景,本文總結(jié)了七種情況,下面進行逐一分析:
一、數(shù)據(jù)庫本身不支持
MySql 的 MyISAM 引擎不支持回滾,如果需要自動回滾事務(wù),需要將MySql的引擎設(shè)置成InnoDB;
二、注解的方法是否為public
//@Transactional注解在private方法上會失效
@Transactional
private void deleteUser() throws MyException{
userMapper.deleteUserA();
int i = 1/0;
userMapper.deleteUserB();
}idea直接會給出提示Methods annotated with ‘@Transactional’ must be overridable ,原理很簡單,private修飾的方式,spring無法生成動態(tài)代理,AOP代理分別在intercept()和invoke()方法判斷是否進行事務(wù)攔截,這兩個方法都會間接調(diào)用AbstractFallbackTransactionAttributeSource類的computeTransactionAttribute方法來獲取事務(wù)控制的相關(guān)屬性。
這其中有以下一段代碼
/**
* Same signature as {@link #getTransactionAttribute}, but doesn't cache the result.
* {@link #getTransactionAttribute} is effectively a caching decorator for this method.
* <p>As of 4.1.8, this method can be overridden.
* @since 4.1.8
* @see #getTransactionAttribute
*/
protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
//...
}這段代碼會導致no-public的方法無法進入事務(wù)控制,所以一定要確保自己需要進行事務(wù)控制的方法包含public修飾符。
三、異常處理不當
當異常被捕獲后,并且沒有再拋出,那么deleteUserA是不會回滾的,例如:
@Transactional
public void deleteUser() {
userMapper.deleteUserA();
try {
int i = 1 / 0;
userMapper.deleteUserB();
} catch (Exception e) {
e.printStackTrace();
}
}異步雖然拋出了,但是拋出的是非RuntimeException類型的異常,依舊不會生效,例如:
@Transactional
public void deleteUser() throws MyException{
userMapper.deleteUserA();
try {
int i = 1 / 0;
userMapper.deleteUserB();
} catch (Exception e) {
throw new MyException();
}
}注解為事務(wù)范圍的方法中,事務(wù)的回滾僅僅對于unchecked的異常有效。對于checked異常無效。也就是說事務(wù)回滾僅僅發(fā)生在,出現(xiàn)RuntimeException或Error的時候。通俗一點就是:代碼中出現(xiàn)的空指針等異常,會被回滾。而文件讀寫、網(wǎng)絡(luò)超時問題等,spring就沒法回滾了。
解決方案:如果指定了回滾異常類型為Exception,那么就可以回滾Checked類型異常了。
@Transactional(rollbackFor = Exception.class)
java里面將派生于Error或者RuntimeException(比如空指針,1/0)的異常稱為unchecked異常,其他繼承自java.lang.Exception得異常統(tǒng)稱為Checked Exception,如IOException、TimeoutException等
四、方法內(nèi)部直接調(diào)用
如果先調(diào)用deleteUser(),那么deleteUserA()是不會回滾的,其原因就是@Transactional根本沒生成代理,例如:
public void deleteUser() throws MyException{
deleteUser2(); // 事物失效
}
@Transactional
public void deleteUser2() throws MyException{
userMapper.deleteUserA();
int i = 1 / 0;
userMapper.deleteUserB();
}五、多數(shù)據(jù)源事物配置問題
項目中沒有配置事務(wù)管理器,需要在配置類或者配置文件中配置,因為項目是多數(shù)據(jù)源的,所以要區(qū)別配置不同數(shù)據(jù)源的事務(wù)管理器,如下:
@Primary
@Bean(name = "db1")
public DataSource getDataSource() {
return createDataSource();
}
@Bean(name = "db1TransactionManager")
public PlatformTransactionManager txManager(@Qualifier("db1") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "db2")
public DataSource getDataSource() {
return buildDataSource();
}
@Bean(name = "db2TransactionManager")
public PlatformTransactionManager txManager(@Qualifier("db2") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}可以看到,兩個事務(wù)管理器配置了不同的beanName,接下來只需要 在需要事務(wù)控制的位置加上該事務(wù)管理器的name就可以完美解決!
@Override
@Transactional(value = "db1TransactionManager",rollbackFor = Exception.class)
public int updateOrInsert(BaseRequest<BankTemplateDto> param) {
...
}六、新開啟一個線程
如下的方式deleteUserA()也不會回滾,因為spring實現(xiàn)事務(wù)的原理是通過ThreadLocal把數(shù)據(jù)庫連接綁定到當前線程中,新開啟一個線程獲取到的連接就不是同一個了,例如:
@Transactional
public void deleteUser() throws MyException{
userMapper.deleteUserA();
try {
//休眠1秒,保證deleteUserA先執(zhí)行
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
int i = 1/0;
userMapper.deleteUserB();
}).start();
}七、事務(wù)傳播屬性設(shè)置錯誤
注意傳播屬性的設(shè)置,一般情況下,propagation屬性無需配置。會使用默認配置,即:PROPAGATION_REQUIRED,有些propagation屬性會導致事務(wù)不會觸發(fā),一定要注意:
- PROPAGATION_SUPPORTS: 如果存在事務(wù),則進入事務(wù);否則,以非事務(wù)方式運行。
- PROPAGATION_NOT_SUPPORTED: 如果存在事務(wù),則掛起事務(wù),并以非事務(wù)方式運行。
- PROPAGATION_NEVER: 以非事務(wù)形式運行,如果存在事務(wù),則拋出異常。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Springboot中實現(xiàn)接口冪等性的4種方案小結(jié)
本文主要介紹了Springboot中實現(xiàn)接口冪等性,包含數(shù)據(jù)庫的冪等,數(shù)據(jù)庫的冪等,Redis的冪等性和Token + 時間戳的冪等性,具有一定的參考價值,感興趣的可以了解一下2024-03-03
Future與FutureTask接口實現(xiàn)示例詳解
這篇文章主要為大家介紹了Future與FutureTask接口實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-10-10
springcloud feign調(diào)其他微服務(wù)時參數(shù)是對象的問題
這篇文章主要介紹了springcloud feign調(diào)其他微服務(wù)時參數(shù)是對象的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03

