springboot中手動提交事務(wù)的實(shí)現(xiàn)方法
演示主要代碼
@Service 層代碼
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.DefaultTransactionDefinition; @Service @Transactional(rollbackFor = Exception.class) public class XlServiceImpl { Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private XlMapper xlMapper; /** * 需求:mi()方法拋出異常,不影響本方法:本方法不回滾! * mi拋出異常后,insert1()正常插入 * @param id * @return * @throws Exception */ public String doInsert() throws Exception { xlMapper.insert1(); mi(); return "200"; } /** * 需求:本方法拋出異常時(shí),回滾 * 拋出異常后,insert2()回滾:不插入 */ private void mi() { xlMapper.insert2(); int x = 0; int y = 3 / x; // } }
Mapper接口層代碼: insert1()和insert2()的插入SQL
import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Update; public interface XlMapper { @Update("UPDATE xl999 SET age=333 WHERE id=#{id}") Integer updateById(Integer id); @Insert("INSERT INTO xl (name,age,create_time) VALUES('dp1',111,NOW())") Integer insert1(); @Insert("INSERT INTO xl (name,age,create_time) VALUES('dp2',222,NOW())") Integer insert2(); }
controller層代碼
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class ClassForTest { Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private XlServiceImpl xlServiceImpl; @GetMapping("/dosth") public String doSth() throws Exception { String affect = ""; affect = xlServiceImpl.doInsert(); return affect; } }
數(shù)據(jù)庫表結(jié)構(gòu)及初始數(shù)據(jù)
場景/需求/實(shí)際效果
場景
在spring的聲明式事務(wù)@Transactional(rollbackFor = Exception.class)
的類XlServiceImpl中:
- 有其中一個(gè)方法
doInsert()
調(diào)用另外一個(gè)方法mi()
。 doInsert()
會調(diào)用Mapper接口的insert1()
方法向數(shù)據(jù)庫中插入一條數(shù)據(jù),然后會調(diào)用方法mi()。mi()
會調(diào)用Mapper接口的insert2()
方法向數(shù)據(jù)庫中插入一條數(shù)據(jù),然后會拋出異常。
需求
insert1()可以正常插入,insert2()回滾,不會插入!
實(shí)際效果
運(yùn)行項(xiàng)目,調(diào)用方法,結(jié)果如下:
頁面:
程序后臺
數(shù)據(jù)庫: 與初始數(shù)據(jù)庫數(shù)據(jù)一致,并沒有數(shù)據(jù)插入——與需求中的 insert1()成功插入不符合。
解決辦法 :在mi方法中手動提交事務(wù)
- 在@Transactional(rollbackFor = Exception.class)類中注入spring的事務(wù)管理器
PlatformTransactionManager
:
/** * 引入 (平臺)事務(wù)管理器,Spring 事務(wù)策略的核心。 */ @Autowired private PlatformTransactionManager transactionManager;
- 在mi方法中手動提交事務(wù)/回滾事務(wù)
/** * 需求:本方法拋出異常時(shí),回滾 拋出異常后,insert2()回滾:不插入 */ private void mi() { DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);// 新發(fā)起一個(gè)事務(wù) TransactionStatus status = transactionManager.getTransaction(def);// 獲得事務(wù)狀態(tài) try { xlMapper.insert2(); int x = 0; int y = 3 / x; // 手動提交事務(wù) transactionManager.commit(status); } catch (Exception e) { // 手動回滾事務(wù) transactionManager.rollback(status); } }
運(yùn)行項(xiàng)目,調(diào)用方法,查看效果:
進(jìn)一步測試:注釋掉下面兩行,效果雖然是一樣的,但是,還是寫上最好??!
transactionManager.commit(status);
transactionManager.rollback(status);
mi()的完整代碼:
/** * 需求:本方法拋出異常時(shí),回滾 拋出異常后,insert2()回滾:不插入 */ private void mi() { DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);// 新發(fā)起一個(gè)事務(wù) TransactionStatus status = transactionManager.getTransaction(def);// 獲得事務(wù)狀態(tài) try { xlMapper.insert2(); int x = 0; int y = 3 / x; // 手動提交事務(wù) // transactionManager.commit(status); } catch (Exception e) { // 手動回滾事務(wù) // transactionManager.rollback(status); } }
以上說明:真正起作用的是下面3行:
DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);// 新發(fā)起一個(gè)事務(wù) TransactionStatus status = transactionManager.getTransaction(def);// 獲得事務(wù)狀態(tài)
而,這3行中又起關(guān)鍵作用的是最后一行 :
TransactionStatus status = transactionManager.getTransaction(def);// 獲得事務(wù)狀態(tài)
通過第2句可知:最后一句是創(chuàng)建一個(gè)新事務(wù)!而這個(gè)新事務(wù)會自動完成提交和回滾,所以注釋掉 提交和回滾的代碼效果是一樣的?。√貏e注意:
- @Transactional(rollbackFor = Exception.class) 方式創(chuàng)建的事務(wù),如果將異常catch后,在catch塊中不再拋出異常,是不會觸發(fā)回滾的!
- 但是,在方法中顯示手動創(chuàng)建一個(gè)事務(wù):
DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);// 新發(fā)起一個(gè)事務(wù) TransactionStatus status = transactionManager.getTransaction(def);// 獲得事務(wù)狀態(tài)
這種手動方式創(chuàng)建的事務(wù),在catch塊中可以不用再拋出異常,也可以不用顯示的寫出:transactionManager.rollback(status); ,會自動進(jìn)行回滾!
在方法中手動創(chuàng)建事務(wù)時(shí),transactionManager.rollback(status); 最多只能執(zhí)行一次,如果方法中有2個(gè)及以上地方調(diào)用的transactionManager.rollback(status); 回滾方法,那么程序就會拋出異常!如下:
在方法中如果執(zhí)行了事務(wù)提交:transactionManager.commit(status); 后面再去執(zhí)行事務(wù)回滾transactionManager.rollback(status);,也會報(bào)上圖的錯(cuò)誤 “事務(wù)已完成——不要再同一個(gè)事務(wù)中提交或回滾超過一次”
也就是說,不管是提交事務(wù),還是回滾事務(wù),二者只能有一個(gè)執(zhí)行并且只能執(zhí)行一次?。。?/p>
Spring的7中事務(wù)傳播行為
Propagation.REQUIRED | 代表當(dāng)前方法支持當(dāng)前的事務(wù),且與調(diào)用者處于同一事務(wù)上下文中,回滾統(tǒng)一回滾(如果當(dāng)前方法是被其他方法調(diào)用的時(shí)候,且調(diào)用者本身即有事務(wù)),如果沒有事務(wù),則自己新建事務(wù), |
---|---|
Propagation.SUPPORTS | 代表當(dāng)前方法支持當(dāng)前的事務(wù),且與調(diào)用者處于同一事務(wù)上下文中,回滾統(tǒng)一回滾(如果當(dāng)前方法是被其他方法調(diào)用的時(shí)候,且調(diào)用者本身即有事務(wù)),如果沒有事務(wù),則該方法在非事務(wù)的上下文中執(zhí)行 |
Propagation.MANDATORY | 代表當(dāng)前方法支持當(dāng)前的事務(wù),且與調(diào)用者處于同一事務(wù)上下文中,回滾統(tǒng)一回滾(如果當(dāng)前方法是被其他方法調(diào)用的時(shí)候,且調(diào)用者本身即有事務(wù)),如果沒有事務(wù),則拋出異常 |
Propagation.REQUIRES_NEW | 創(chuàng)建一個(gè)新的事務(wù)上下文,如果當(dāng)前方法的調(diào)用者已經(jīng)有了事務(wù),則掛起調(diào)用者的事務(wù),這兩個(gè)事務(wù)不處于同一上下文,如果各自發(fā)生異常,各自回滾 |
Propagation.NOT_SUPPORTED | 該方法以非事務(wù)的狀態(tài)執(zhí)行,如果調(diào)用該方法的調(diào)用者有事務(wù)則先掛起調(diào)用者的事務(wù) |
Propagation.NEVER | 該方法以非事務(wù)的狀態(tài)執(zhí)行,如果調(diào)用者存在事務(wù),則拋出異常 |
Propagation.NESTED | 如果當(dāng)前上下文中存在事務(wù),則以嵌套事務(wù)執(zhí)行該方法,也就說,這部分方法是外部方法的一部分,調(diào)用者回滾,則該方法回滾,但如果該方法自己發(fā)生異常,則自己回滾,不會影響外部事務(wù),如果不存在事務(wù),則與PROPAGATION_REQUIRED一樣 |
到此這篇關(guān)于springboot中手動提交事務(wù)的實(shí)現(xiàn)方法的文章就介紹到這了,更多相關(guān)springboot 手動提交事務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中的NoSuchMethodException異常原因以及解決方案詳解
這篇文章主要介紹了Java中的NoSuchMethodException異常原因以及解決方案詳解,NoSuchMethodException是Java反射機(jī)制中的異常,在嘗試通過反射獲取方法時(shí),找不到指定的方法,通常發(fā)生在調(diào)用?Class?對象的方法時(shí),當(dāng)方法名或方法參數(shù)不匹配時(shí)拋出該異常,需要的朋友可以參考下2024-02-02springboot集成druid,多數(shù)據(jù)源可視化,p6spy問題
這篇文章主要介紹了springboot集成druid,多數(shù)據(jù)源可視化,p6spy問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01SpringBoot?整合Redis?數(shù)據(jù)庫的方法
Redis是一個(gè)基于內(nèi)存的日志型可持久化的緩存數(shù)據(jù)庫,保存形式為key-value格式,Redis完全免費(fèi)開源,它使用ANSI?C語言編寫。這篇文章主要介紹了SpringBoot?整合Redis?數(shù)據(jù)庫的方法,需要的朋友可以參考下2018-03-03idea?2024使用Maven創(chuàng)建Java?Web項(xiàng)目詳細(xì)圖文教程
這篇文章主要給大家介紹了關(guān)于idea?2024使用Maven創(chuàng)建Java?Web項(xiàng)目的相關(guān)資料,介紹了如何使用Maven創(chuàng)建一個(gè)Spring?MVC項(xiàng)目,并配置Tomcat服務(wù)器以運(yùn)行一個(gè)簡單的Helloworld?JSP頁面,需要的朋友可以參考下2024-12-12java中用ObjectMapper類實(shí)現(xiàn)Json與bean的轉(zhuǎn)換示例
這篇文章主要給大家介紹了關(guān)于在java中用ObjectMapper類實(shí)現(xiàn)Json與bean轉(zhuǎn)換的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-08-08SpringSecurity獲取當(dāng)前登錄用戶的信息的幾種方法實(shí)現(xiàn)
本文主要介紹了SpringSecurity中獲取當(dāng)前登錄用戶信息的多種方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-03-03