spring中@Transactional?注解失效的原因及解決辦法
前言
面試中經(jīng)常會(huì)被問(wèn)到事務(wù)失效的場(chǎng)景有哪些,其實(shí)在開(kāi)發(fā)中,若是不了解事務(wù)失效的場(chǎng)景,當(dāng)你覺(jué)得加了事務(wù),就會(huì)回滾,就大錯(cuò)特錯(cuò)了,今天就來(lái)了解一下吧。
一、@Transactional 屬性介紹
1.事務(wù)的傳播行為:propagation
這就是我們常說(shuō)的事務(wù)的七種傳播行為,默認(rèn)值:Propagation.REQUIRED
屬性 | 解釋 |
---|---|
Propagation.REQUIRED | 如果當(dāng)前存在事務(wù),則加入該事務(wù),如果當(dāng)前不存在事務(wù),則創(chuàng)建一個(gè)新的事務(wù)。 |
Propagation.SUPPORTS | 如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前不存在事務(wù),則以非事務(wù)的方式繼續(xù)運(yùn)行。 |
Propagation.MANDATORY | 如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前不存在事務(wù),則拋出異常。 |
Propagation.REQUIRES_NEW | 重新創(chuàng)建一個(gè)新的事務(wù),如果當(dāng)前存在事務(wù),暫停當(dāng)前的事務(wù)。 |
Propagation.NOT_SUPPORTED | 以非事務(wù)的方式運(yùn)行,如果當(dāng)前存在事務(wù),暫停當(dāng)前的事務(wù)。 |
Propagation.NEVER | 以非事務(wù)的方式運(yùn)行,如果當(dāng)前存在事務(wù),則拋出異常。 |
Propagation.NESTED | 嵌套事務(wù)。 |
2.事務(wù)的隔離級(jí)別:isolation
這就是我們常說(shuō)的事務(wù)的隔離級(jí)別,默認(rèn)值:Isolation.DEFAULT
屬性 | 解釋 |
---|---|
Isolation.DEFAULT | 使用底層數(shù)據(jù)庫(kù)默認(rèn)的隔離級(jí)別 |
Isolation.READ_UNCOMMITTED | 讀未提交 |
Isolation.READ_COMMITTED | 讀已提交 |
Isolation.REPEATABLE_READ | 可重復(fù)讀 |
Isolation.SERIALIZABLE | 串行化 |
3.事務(wù)的超時(shí)時(shí)間:timeout
- timeout :事務(wù)的超時(shí)時(shí)間,默認(rèn)值為 -1。如果超過(guò)該時(shí)間限制但事務(wù)還沒(méi)有完成,則自動(dòng)回滾事務(wù)。
4.事務(wù)的回滾類型:rollbackFor
- rollbackFor :用于指定能夠觸發(fā)事務(wù)回滾的異常類型,可以指定多個(gè)異常類型。
二、@Transactional 失效場(chǎng)景
1.同一個(gè)類中方法調(diào)用,注解失效
場(chǎng)景重現(xiàn):當(dāng)調(diào)用 saveCabinet 方法時(shí),save 方法中出異常不會(huì)回滾,如下代碼
@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; // 模擬出錯(cuò) } }
原因分析:spring 中采用動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)事務(wù),當(dāng)同一個(gè)類調(diào)用 save 時(shí),不是代理類在調(diào)用,所以掃描不到事務(wù)注解
解決方案1:將 save 方法提到另一個(gè)類中,saveCabinet 調(diào)用另一個(gè)類的 save 方法(推薦使用)
// 當(dāng)前類 @Service public class CabinetServiceImpl implements CabinetService { @Autowired private MethodTest methodTest; @Override public void saveCabinet(Cabinet cabinet) { methodTest.save(cabinet); } } // 另一個(gè)類 @Service public class MethodTest { @Autowired private CabinetMapper cabinetMapper; @Transactional(rollbackFor = Exception.class) public void save(Cabinet cabinet) { cabinetMapper.insert(cabinet); int i = 1/0; // 模擬出錯(cuò) } }
解決方案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; // 模擬出錯(cuò) } }
2.異常被 catch “吃了”,注解失效
場(chǎng)景重現(xiàn):當(dāng)調(diào)用 saveCabinet 方法時(shí),方法體被 try-catch 了,不會(huì)回滾,如下代碼
@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; // 模擬出錯(cuò) } catch (Exception e) { e.printStackTrace(); } } }
原因分析:異常直接捕獲沒(méi)有向上拋出,而是自己處理了,注解捕獲不到異常,所以失效
解決方案:將 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; // 模擬出錯(cuò) } catch (Exception e) { e.printStackTrace(); throw new Exception(e); } } }
3.@Transactional 應(yīng)用在非 public 修飾的方法上
- 場(chǎng)景重現(xiàn):當(dāng)調(diào)用的方法不是 public 修飾時(shí),不會(huì)回滾,事務(wù)失效
- 原因分析:在 spring aop 代理時(shí),事務(wù)攔截器會(huì)在目標(biāo)方法的執(zhí)行前后進(jìn)行攔截,其中的方法會(huì)去獲取 Transactional 注解的一些信息,而其中的方法會(huì)去檢查目標(biāo)方法是否被 public 修飾,不是的話獲取不到注解信息
- 解決方案:將調(diào)用的方法訪問(wèn)修飾符改為 public
4.@Transactional 注解屬性 rollbackFor 設(shè)置錯(cuò)誤
場(chǎng)景重現(xiàn):當(dāng)注解沒(méi)有指定 rollbackFor 屬性時(shí),如下若拋出 FileNotFoundException 異常則不會(huì)回滾
@Service public class CabinetServiceImpl implements CabinetService { @Autowired private CabinetMapper cabinetMapper; @Override @Transactional public void saveCabinet(Cabinet cabinet) throws IOException { cabinetMapper.insert(cabinet); // 模擬出錯(cuò) throw new FileNotFoundException(); } }
原因分析:spring 默認(rèn)拋出未檢查異常,繼承 RuntimeException 或者 Error 才會(huì)回滾,其他異常如 FileNotFoundException 則不會(huì)回滾
解決方案:指定 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); // 模擬出錯(cuò) throw new FileNotFoundException(); } }
5.@Transactional 注解屬性 propagation 設(shè)置錯(cuò)誤
- 使用了不支持事務(wù)的傳播屬性
6.數(shù)據(jù)庫(kù)引擎不支持事務(wù)
- 數(shù)據(jù)庫(kù)不支持事務(wù)
總結(jié)
到此這篇關(guān)于spring中@Transactional 注解失效的原因及解決辦法的文章就介紹到這了,更多相關(guān)spring @Transactional 失效內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot排除自動(dòng)加載數(shù)據(jù)源方式
這篇文章主要介紹了SpringBoot排除自動(dòng)加載數(shù)據(jù)源方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05將本地SpringBoot項(xiàng)目發(fā)布到云服務(wù)器的方法
這篇文章主要介紹了如何將本地SpringBoot項(xiàng)目發(fā)布到云服務(wù)器,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-12-12Java中List排序的3種常見(jiàn)方法總結(jié)
在Java編程中List對(duì)象的排序是一個(gè)常見(jiàn)的需求,List接口提供了多種排序方法,這篇文章主要給大家介紹了關(guān)于Java中List排序的3種常見(jiàn)方法,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-08-08SpringMVC整合SSM實(shí)現(xiàn)表現(xiàn)層數(shù)據(jù)封裝詳解
這篇文章主要介紹了SpringMVC整合SSM實(shí)現(xiàn)表現(xiàn)層數(shù)據(jù)封裝,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2022-10-10spring整合shiro框架的實(shí)現(xiàn)步驟記錄
Shiro是一個(gè)強(qiáng)大易用的Java安全框架,提供了認(rèn)證、授權(quán)、加密和會(huì)話管理等功能。下面這篇文章主要給大家介紹了關(guān)于spring整合shiro框架的實(shí)現(xiàn)步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2018-05-05java多線程之并發(fā)工具類CountDownLatch,CyclicBarrier和Semaphore
這篇文章主要為大家介紹了java并發(fā)工具類CountDownLatch,CyclicBarrier和Semaphore ,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2021-12-12Spring mvc攔截器實(shí)現(xiàn)原理解析
這篇文章主要介紹了Spring mvc攔截器實(shí)現(xiàn)原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03java web中 HttpClient模擬瀏覽器登錄后發(fā)起請(qǐng)求
這篇文章主要介紹了java web中 HttpClient模擬瀏覽器登錄后發(fā)起請(qǐng)求的相關(guān)資料,需要的朋友可以參考下2017-05-05