spring中@Transactional?注解失效的原因及解決辦法
前言
面試中經(jīng)常會被問到事務失效的場景有哪些,其實在開發(fā)中,若是不了解事務失效的場景,當你覺得加了事務,就會回滾,就大錯特錯了,今天就來了解一下吧。
一、@Transactional 屬性介紹
1.事務的傳播行為:propagation
這就是我們常說的事務的七種傳播行為,默認值:Propagation.REQUIRED
| 屬性 | 解釋 |
|---|---|
| Propagation.REQUIRED | 如果當前存在事務,則加入該事務,如果當前不存在事務,則創(chuàng)建一個新的事務。 |
| Propagation.SUPPORTS | 如果當前存在事務,則加入該事務;如果當前不存在事務,則以非事務的方式繼續(xù)運行。 |
| Propagation.MANDATORY | 如果當前存在事務,則加入該事務;如果當前不存在事務,則拋出異常。 |
| Propagation.REQUIRES_NEW | 重新創(chuàng)建一個新的事務,如果當前存在事務,暫停當前的事務。 |
| Propagation.NOT_SUPPORTED | 以非事務的方式運行,如果當前存在事務,暫停當前的事務。 |
| Propagation.NEVER | 以非事務的方式運行,如果當前存在事務,則拋出異常。 |
| Propagation.NESTED | 嵌套事務。 |
2.事務的隔離級別:isolation
這就是我們常說的事務的隔離級別,默認值:Isolation.DEFAULT
| 屬性 | 解釋 |
|---|---|
| Isolation.DEFAULT | 使用底層數(shù)據(jù)庫默認的隔離級別 |
| Isolation.READ_UNCOMMITTED | 讀未提交 |
| Isolation.READ_COMMITTED | 讀已提交 |
| Isolation.REPEATABLE_READ | 可重復讀 |
| Isolation.SERIALIZABLE | 串行化 |
3.事務的超時時間:timeout
- timeout :事務的超時時間,默認值為 -1。如果超過該時間限制但事務還沒有完成,則自動回滾事務。
4.事務的回滾類型:rollbackFor
- rollbackFor :用于指定能夠觸發(fā)事務回滾的異常類型,可以指定多個異常類型。
二、@Transactional 失效場景
1.同一個類中方法調(diào)用,注解失效
場景重現(xiàn):當調(diào)用 saveCabinet 方法時,save 方法中出異常不會回滾,如下代碼
@Service
public class CabinetServiceImpl implements CabinetService {
@Autowired
private CabinetMapper cabinetMapper;
@Override
public void saveCabinet(Cabinet cabinet) {
save(cabinet);
}
@Transactional(rollbackFor = Exception.class)
public void save(Cabinet cabinet) {
cabinetMapper.insert(cabinet);
int i = 1/0; // 模擬出錯
}
}
原因分析:spring 中采用動態(tài)代理來實現(xiàn)事務,當同一個類調(diào)用 save 時,不是代理類在調(diào)用,所以掃描不到事務注解
解決方案1:將 save 方法提到另一個類中,saveCabinet 調(diào)用另一個類的 save 方法(推薦使用)
// 當前類
@Service
public class CabinetServiceImpl implements CabinetService {
@Autowired
private MethodTest methodTest;
@Override
public void saveCabinet(Cabinet cabinet) {
methodTest.save(cabinet);
}
}
// 另一個類
@Service
public class MethodTest {
@Autowired
private CabinetMapper cabinetMapper;
@Transactional(rollbackFor = Exception.class)
public void save(Cabinet cabinet) {
cabinetMapper.insert(cabinet);
int i = 1/0; // 模擬出錯
}
}
解決方案2:在 CabinetServiceImpl 中自己注入自己,用自己的代理類調(diào)用 save 方法,即 cabinetServiceImpl.save()
@Service
public class CabinetServiceImpl implements CabinetService {
@Autowired
private CabinetMapper cabinetMapper;
@Autowired
private CabinetServiceImpl cabinetServiceImpl;
@Override
public void saveCabinet(Cabinet cabinet) {
cabinetServiceImpl.save(cabinet);
}
@Transactional(rollbackFor = Exception.class)
public void save(Cabinet cabinet) {
cabinetMapper.insert(cabinet);
int i = 1/0; // 模擬出錯
}
}
2.異常被 catch “吃了”,注解失效
場景重現(xiàn):當調(diào)用 saveCabinet 方法時,方法體被 try-catch 了,不會回滾,如下代碼
@Service
public class CabinetServiceImpl implements CabinetService {
@Autowired
private CabinetMapper cabinetMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public void saveCabinet(Cabinet cabinet) {
try {
cabinetMapper.insert(cabinet);
int i = 1/0; // 模擬出錯
} catch (Exception e) {
e.printStackTrace();
}
}
}
原因分析:異常直接捕獲沒有向上拋出,而是自己處理了,注解捕獲不到異常,所以失效
解決方案:將 catch 到的異常向上拋出 throw new Exception(e);,異常交給注解處理
@Service
public class CabinetServiceImpl implements CabinetService {
@Autowired
private CabinetMapper cabinetMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public void saveCabinet(Cabinet cabinet) throws Exception {
try {
cabinetMapper.insert(cabinet);
int i = 1/0; // 模擬出錯
} catch (Exception e) {
e.printStackTrace();
throw new Exception(e);
}
}
}
3.@Transactional 應用在非 public 修飾的方法上
- 場景重現(xiàn):當調(diào)用的方法不是 public 修飾時,不會回滾,事務失效
- 原因分析:在 spring aop 代理時,事務攔截器會在目標方法的執(zhí)行前后進行攔截,其中的方法會去獲取 Transactional 注解的一些信息,而其中的方法會去檢查目標方法是否被 public 修飾,不是的話獲取不到注解信息
- 解決方案:將調(diào)用的方法訪問修飾符改為 public
4.@Transactional 注解屬性 rollbackFor 設置錯誤
場景重現(xiàn):當注解沒有指定 rollbackFor 屬性時,如下若拋出 FileNotFoundException 異常則不會回滾
@Service
public class CabinetServiceImpl implements CabinetService {
@Autowired
private CabinetMapper cabinetMapper;
@Override
@Transactional
public void saveCabinet(Cabinet cabinet) throws IOException {
cabinetMapper.insert(cabinet);
// 模擬出錯
throw new FileNotFoundException();
}
}
原因分析:spring 默認拋出未檢查異常,繼承 RuntimeException 或者 Error 才會回滾,其他異常如 FileNotFoundException 則不會回滾
解決方案:指定 rollbackFor 屬性,一般指定 @Transactional(rollbackFor = Exception.class) 即可,也可以指定自定義異常
@Service
public class CabinetServiceImpl implements CabinetService {
@Autowired
private CabinetMapper cabinetMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public void saveCabinet(Cabinet cabinet) throws IOException {
cabinetMapper.insert(cabinet);
// 模擬出錯
throw new FileNotFoundException();
}
}
5.@Transactional 注解屬性 propagation 設置錯誤
- 使用了不支持事務的傳播屬性
6.數(shù)據(jù)庫引擎不支持事務
- 數(shù)據(jù)庫不支持事務
總結(jié)
到此這篇關(guān)于spring中@Transactional 注解失效的原因及解決辦法的文章就介紹到這了,更多相關(guān)spring @Transactional 失效內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Netty分布式高性能工具類recycler的使用及創(chuàng)建
這篇文章主要為大家介紹了Netty分布式高性能工具類recycler的使用和創(chuàng)建,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步2022-03-03
Java語言實現(xiàn)非遞歸實現(xiàn)樹的前中后序遍歷總結(jié)
今天小編就為大家分享一篇關(guān)于Java語言實現(xiàn)非遞歸實現(xiàn)樹的前中后序遍歷總結(jié),小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-01-01
SpringBoot事件監(jiān)聽器@EventListener的實現(xiàn)
@EventListener注解用于處理應用程序事件,提供了一種方便的方式來監(jiān)聽和響應各種事件,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2024-11-11

