SpringBoot中的事務(wù)配置管理詳解
Spring Boot事務(wù)配置管理
場(chǎng)景:我們?cè)陂_(kāi)發(fā)企業(yè)應(yīng)用時(shí),由于數(shù)據(jù)操作在順序執(zhí)行的過(guò)程中,線上可能有各種無(wú)法預(yù)知的問(wèn)題,
任何一步操作都有可能發(fā)生異常,異常則會(huì)導(dǎo)致后續(xù)的操作無(wú)法完成。此時(shí)由于業(yè)務(wù)邏輯并未正確的完
成,所以在之前操作過(guò)數(shù)據(jù)庫(kù)的動(dòng)作并不可靠,需要在這種情況下進(jìn)行數(shù)據(jù)的回滾。
1. 導(dǎo)入依賴(lài)
springboot的事務(wù)管理需要導(dǎo)入spring-boot-starter-jdbc;而我們導(dǎo)入的mybatis-spring-boot-starter包含了它,所以無(wú)需重復(fù)導(dǎo)入;
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.1</version> </dependency>
2. 事務(wù)測(cè)試
1)mapper接口
@Insert("insert into tb_filminfo (typeid,filmname,ticketprice) values(#{typeid},#{filmname},#{ticketprice})") int insert(FilminfoPO po);
2)service接口
public interface IFilmInfoService { //插入一條記錄 public int insert(FilminfoPO po);
3)service實(shí)現(xiàn)類(lèi)
@Service public class FilmInfoServiceImpl implements IFilmInfoService { @Resource private FilmInfoMapper mapper; @Transactional @Override public int insert(FilminfoPO po) { return mapper.insert(po); }
4)controller類(lèi)
@Controller @RequestMapping("/film") public class FilmInfoController { @Resource IFilmInfoService service; @RequestMapping("/insert") public String insert(FilminfoPO po) { if (po!=null) { int i = service.insert(po); return "success"; } else { return "false"; } } }
當(dāng)沒(méi)有異常拋出時(shí)添加成功,有異常出現(xiàn)添加失??;
3. 事務(wù)處理的一些特殊情況
1)異常并沒(méi)有被捕獲到
異常并沒(méi)有被 ”捕獲“ 到,導(dǎo)致事務(wù)并沒(méi)有回滾。
Spring Boot 默認(rèn)的事務(wù)規(guī)則是遇到運(yùn)行異常(RuntimeException)和程序 錯(cuò)誤(Error)才會(huì)回滾。但是拋出 SQLException 就無(wú)法回滾了。
@Transactional @Override public void insert(FilminfoPO po) throws SQLException { // 手動(dòng)拋出異常 mapper.insert(po); throw new SQLException("數(shù)據(jù)庫(kù)異常");
雖然拋出異常但是數(shù)據(jù)插入成功;
解決方案:針對(duì)非運(yùn)行時(shí)異常,如果要進(jìn)行事務(wù)回滾的話,可以在 @Transactional 注解中使用 rollbackFor 屬性來(lái)指定異常,比如 @Transactional(rollbackFor = Exception.class) ,這樣就沒(méi)有問(wèn)題了,所以在實(shí)際項(xiàng)目中,一定要指定異常。
@Transactional(rollbackFor = Exception.class) @Override public void insert(FilminfoPO po) throws SQLException { // 手動(dòng)拋出異常 mapper.insert(po); throw new SQLException("數(shù)據(jù)庫(kù)異常"); }
這樣就插入失敗了;
2)異常在方法中被捕獲導(dǎo)致事務(wù)回滾失敗
我們?cè)谔幚懋惓r(shí),有兩種方式, 要么拋出去,讓上一層來(lái)捕獲處理;要么把異常 try catch 掉,在異常出現(xiàn)的地方給處理掉。就因?yàn)橛?這中 try…catch,所以導(dǎo)致異常被 ”吃“ 掉,事務(wù)無(wú)法回滾。
@Transactional @Override public int insert(FilminfoPO po) { try { int i = 1 / 0; } catch (Exception e) { e.getMessage(); } return mapper.insert(po); }
記錄成功被插入;
解決方法:直接往上拋,給上一層來(lái)處理即可
3)事務(wù)的范圍 沖突導(dǎo)致回滾失敗
許多業(yè)務(wù)需要在高并發(fā)的情況下保證數(shù)據(jù)唯一性所比要加synchronized關(guān)鍵字如一個(gè)數(shù)據(jù)庫(kù)中,針對(duì)某個(gè)用戶,只有一條記錄,下一個(gè)插入動(dòng)作過(guò)來(lái),會(huì)先判斷該數(shù)據(jù)庫(kù) 中有沒(méi)有相同的用戶,如果有就不插入,就更新,沒(méi)有才插入,所以理論上,數(shù)據(jù)庫(kù)中永遠(yuǎn)就一條同一 用戶信息,不會(huì)出現(xiàn)同一數(shù)據(jù)庫(kù)中插入了兩條相同用戶的信息。
@Transactional(rollbackFor = Exception.class) @Override public synchronized void insert(FilminfoPO po) throws SQLException { // 手動(dòng)拋出異常 mapper.insert(po); }
但是在壓測(cè)時(shí),數(shù)據(jù)庫(kù)中確實(shí)可能有兩條同一用戶的信息,分析其原因,在于事務(wù)的
范圍和鎖的范圍問(wèn)題。
在執(zhí)行該方法開(kāi)始時(shí),事務(wù)啟動(dòng),執(zhí)行 完了后,事務(wù)關(guān)閉。但是 synchronized 沒(méi)有起作用,其實(shí)根本原因是因?yàn)槭聞?wù)的范圍比鎖的范圍大。 也就是說(shuō),在加鎖的那部分代碼執(zhí)行完之后,鎖釋放掉了,但是事務(wù)還沒(méi)結(jié)束,此時(shí)另一個(gè)線程進(jìn)來(lái) 了,事務(wù)沒(méi)結(jié)束的話,第二個(gè)線程進(jìn)來(lái)時(shí),數(shù)據(jù)庫(kù)的狀態(tài)和第一個(gè)線程剛進(jìn)來(lái)是一樣的。即由于mysql Innodb引擎的默認(rèn)隔離級(jí)別是可重復(fù)讀(在同一個(gè)事務(wù)里,SELECT的結(jié)果是事務(wù)開(kāi)始時(shí)時(shí)間點(diǎn)的狀 態(tài)),線程二事務(wù)開(kāi)始的時(shí)候,線程一還沒(méi)提交完成,導(dǎo)致讀取的數(shù)據(jù)還沒(méi)更新。第二個(gè)線程也做了插 入動(dòng)作,導(dǎo)致了臟數(shù)據(jù)。
解決方案:
? 1.把事務(wù)去掉即可(不推薦);
? 2. 在調(diào)用該 service 的地方加鎖,保證鎖 的范圍比事務(wù)的范圍大即可。
到此這篇關(guān)于SpringBoot中的事務(wù)配置管理詳解的文章就介紹到這了,更多相關(guān)SpringBoot事務(wù)配置內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot整合Mybatis-plus實(shí)現(xiàn)多級(jí)評(píng)論功能
本文介紹了如何使用SpringBoot整合Mybatis-plus實(shí)現(xiàn)多級(jí)評(píng)論功能,同時(shí)提供了數(shù)據(jù)庫(kù)的設(shè)計(jì)和詳細(xì)的后端代碼,前端界面使用的Vue2,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-05-05Java基礎(chǔ)之JDBC的數(shù)據(jù)庫(kù)連接與基本操作
這篇文章主要介紹了Java基礎(chǔ)之JDBC的數(shù)據(jù)庫(kù)連接與基本操作,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java基礎(chǔ)的小伙伴們也有很好的幫助,需要的朋友可以參考下2021-05-05Java String類(lèi)字符串的理解與認(rèn)知
String字符串和char字符不同,char使用單引號(hào),只能表示一個(gè)字符,字符串就是一段文本。String是個(gè)類(lèi)。這個(gè)類(lèi)使用final修飾,所以這個(gè)類(lèi)是不可以繼承擴(kuò)充和修改它的方法的2021-10-10Java兩種方式實(shí)現(xiàn)動(dòng)態(tài)代理
Java 在 java.lang.reflect 包中有自己的代理支持,該類(lèi)(Proxy.java)用于動(dòng)態(tài)生成代理類(lèi),只需傳入目標(biāo)接口、目標(biāo)接口的類(lèi)加載器以及 InvocationHandler 便可為目標(biāo)接口生成代理類(lèi)及代理對(duì)象。我們稱(chēng)這個(gè)Java技術(shù)為:動(dòng)態(tài)代理2020-10-10詳解Spring3.x 升級(jí)至 Spring4.x的方法
本篇文章主要介紹了詳解Spring3.x 升級(jí)至 Spring4.x的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-04-04Spring?Data?JPA命名約定查詢實(shí)現(xiàn)方法
這篇文章主要為大家介紹了Spring?Data?JPA命名約定查詢實(shí)現(xiàn)方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12