springboot事務失效的日常總結(@Transactional)
什么是事務
事務(Transaction),一般是指要做的或所做的事情。在計算機術語中是指訪問並可能更新數據庫中各種數據項的一個程序執行單元(unit)。
事務是恢復和並發控制的基本單位
事務應該具有4個屬性:(這里指的是單機,不是分布式)
原子性、一致性、隔離性、持久性。這四個屬性通常稱為ACID特性(單講一篇)
說人話就是:
數據庫一次執行數據的單元(要么這個事務(執行的sql)都成功,要么都失敗)
為什么使用事務
說白了就是為了讓這批次同時成功,或同時失敗(也就是有個后悔的時候)
什么時候使用事務
場景一:如果實際的業務中,需要將一條數據同時存放到兩張表中, 並且要求兩張表中的數據同步,那么此時就需要使用事務管理機制,保證數據同步。如果出現錯誤情況,比如表一插入數據成功,表二插入數據失敗,那么就回滾,終止數據持久化操作。
場景二:金融行業的軟件開發嚴格重視事務處理,比如我們常見的轉賬操作,一方的賬戶金額減少,對應的是另一方的賬戶金額增加,這個過程需要使用到事務機制,不然轉賬不能成功
場景三:要求同一批次的提交需要一起成功,或一起失敗
這個寫的比較直白,做個備份:
Java中為什么使用事務?什么時候使用事務?如何使用事務? - Java精進之路 - 博客園 (cnblogs.com)
spring的事務:
編程式事務:
指在代碼中手動的管理事務的提交,回滾等操作。
優點:可以自己控制事務的范圍,適合異步的情況(可以指定代碼行)
缺點:代碼侵入性比較高
聲明式事務:
是面向AOP切面的。將具體的業務和事務處理進行解耦,代碼侵入性比較低。使用的比較普遍。
優點:耦合度低,使用簡單(一般使用@Transactional)
缺點:只能使用在類,接口和方法上。使用的顆粒度比較高。不適合使用存在有異步調用的情況(接口不推薦使用這個)
@Transactional事務的失效
1. @Transactional 應用在非 public 修飾的方法上
因為@Transactional 的工作原理是基於AOP來實現的,所以,必須作用在public的方法上才行
protected
、private
修飾的方法上使用@Transactional
注解,雖然事務無效,但不會有任何報錯
2.@Transactional 注解屬性 propagation 設置錯誤
- TransactionDefinition.PROPAGATION_SUPPORTS:如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續運行。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務方式運行,如果當前存在事務,則把當前事務掛起。
- TransactionDefinition.PROPAGATION_NEVER:以非事務方式運行,如果當前存在事務,則拋出異常
以上這三種配置,是會導致注解失效的
3.@Transactional 注解屬性 rollbackFor 設置錯誤
rollbackFor 指定回滾的情況。spring默認是回滾RuntimeException或這error才回滾。當然自定義的RuntimeException異常類也是可以的。
如果希望spring能夠回滾別類型的異常,那就需要使用rollbackFor去指定(當然如果是指定異常的子類,也同樣會回滾)
@Transactional(rollbackFor=Exception.class)
4. 同一個類中方法調用,導致@Transactional失效
其實這還是由於使用Spring AOP代理造成的,因為只有當事務方法被當前類以外的代碼調用時,才會由Spring生成的代理對象來管理
開發中避免不了會對同一個類里面的方法調用,比如有一個類Test,它的一個方法A,A再調用本類的方法B(不論方法B是用public還是private修飾),但方法A沒有聲明注解事務,而B方法有。則外部調用方法A之后,方法B的事務是不會起作用的。這也是經常犯錯誤的一個地方
public void A() throws Exception {
/**
* 調用B方法
*/
this.B();
... ...
}
@Transactional()
public void B() throws Exception {
mapper.insert();
}
處理辦法:
處理方式:
/ /自己注入自己
@Autowired
A a ;然后用對象訪問
5.異常被你的 catch“吃了
這個就比較簡單了,就是你自己捕捉到了異常,並且自己處理,並不會拋出到上層的方法調用。那就不會生效了
try { nowDate.setTime(1623832800000L); } catch (Exception e) { System.out.println(e.getMessage()); }
6.數據庫不支持事務
如mysql的數據庫引擎,innodb支持事務,而myisam就不支持。
7.新開啟一個線程
spring實現事務的原理是通過ThreadLocal把數據庫連接綁定到當前線程中,新開啟一個線程獲取到的連接就不是同一個了。
@Transactional
public void deleteUser() throws MyException{
userMapper.deleteUserA();
try {
//休眠1秒,保證deleteUserA先執行
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
int i = 1/0;
userMapper.deleteUserB();
}).start();
}
這里備注一個寫的比較好的博客: