您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關如何解決spring聲明式事務@Transactional不回滾的多種情況問題的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
一、Spring事務原理
在使用JDBC事務操作數據庫時,流程如下:
//獲取連接 1.Connection con = DriverManager.getConnection() //開啟事務 2.con.setAutoCommit(true/false); 3.執行CRUD //提交事務/回滾事務 4. con.commit() / con.rollback(); //關閉連接 5. conn.close();
Spring本身并不提供事務,而是對JDBC事務通過AOP做了封裝,隱藏了2和4的操作,簡化了JDBC的應用。
spring對JDBC事務的封裝,是通過AOP動態代理來實現的,在調用目標方法(也就是第3步)前后會通過代理類來執行事務的開啟、提交或者回滾操作。
spring事務使用的兩個不可忽略點:
注意關鍵詞 “動態代理”,這意味著要生成一個代理類,那么我們就不能在一個類內直接調用事務方法,否則無法代理,而且該事務方法必須是public,如果定義成 protected、private 或者默認可見性,則無法調用!
@Transactional 最好加到service層,加到Controller層也是生效的,但是為了規范起見,還是加到service層上。
下載代碼并啟動難項目進行驗證:主要代碼如下:
Controller層代碼如下:
@Autowired private TransactionalService transactionalService; @Autowired private UserDao userDao; @Autowired private JwtUserDao jwtUserDao; /** * 測試@Transactional 注解加到service層事務是否回滾 */ @RequestMapping("/tx") public void serviceTX(){ transactionalService.controllerTX(); } /** * 測試@Transactional 注解加到Controller層事務是否回滾 */ @Transactional(rollbackFor = Exception.class) @RequestMapping("/ctx2") public void cTX2(){ userDao.update(); System.out.println(2/0); jwtUserDao.update(); } /** * 測試@Transactional 注解加到Controller層事務是否回滾 * 這里在Controller層為了方便直接調用了dao層,在實際開發中dao層即可在Controller層調用也可以在service層調用, * 比如service層只是直接調用dao層一個方法,此外沒有任何操作,那么這時候完全不用寫service層的方法,直接在Controller調用dao層即可, * 當然如果公司有規范,必須嚴格按照mvc的模式進行開發,則另說 */ @Transactional(rollbackFor = Exception.class) @RequestMapping("/ctx2") public void cTX2(){ userDao.update(); //手動拋出一個RuntimeException System.out.println(2/0); jwtUserDao.update(); }
service層的主要代碼
@Autowired private UserDao userDao; @Autowired private JwtUserDao jwtUserDao; @Transactional(rollbackFor = Exception.class) public void controllerTX(){ userDao.update(); //手動拋出一個RuntimeException System.out.println(2/0); jwtUserDao.update(); }
dao層sql語句如下:
<update id="update"> UPDATE jwt_user SET username ='wangwuupdate' WHERE user_id= 2 </update> <update id="update"> UPDATE user SET username ='zsupdate' WHERE id= 2 </update>
數據庫原始數據:
瀏覽器中輸入:http://localhost:8081/tx/tx,由于本次使用測試代碼進行統一的異常處理所以瀏覽器的返回數據如下:
控制臺輸出如下:
查看數據庫中的數據并沒有被修改
瀏覽器中輸入:http://localhost:8081/tx/ctx2,
查看數據庫中的數據并沒有被修改
由此可以得出 :@Transactional 加到Controller層也是生效的,但是為了規范起見,還是加到service層上。
spring的api doc中有折磨一句描述:
紅框中的內容如下:
rolling back on RuntimeException and Error but not on checked exceptions
大致意思就默認情況下,當程序發生 RuntimeException 和 Error 的這兩種異常的時候事務會回滾,但是如果發生了checkedExcetions ,如fileNotfundException 則不會回滾,所以 rollbackFor = Exception.class 這個一定要加!
驗證如下:
瀏覽器輸入:http://localhost:8081/tx/ctx3
控制臺輸出如下:
這時候查看數據庫中的數據并沒有被修改
瀏覽器輸入:http://localhost:8081/tx/ctx4
這時候查看數據庫數據已經被修改:
/** * 同類中在方法a中調用b * a沒有事務,b有 ,異常發生在b中 不會回滾 */ @RequestMapping("/a1") public void a1(){ transactionalService.a1(); } /** * 同類中在方法a中調用b * a沒有事務,b有 ,異常發生在a中 不會回滾 */ @RequestMapping("/a2") public void a2(){ transactionalService.a2(); } /** * 同類中在方法a中調用b * a有事務,b沒有 ,異常發生在b中 會回滾 */ @RequestMapping("/a3") public void a3(){ transactionalService.a3(); } /** * 同類中在方法a中調用b * a有事務,b沒有 ,異常發生在a中 會回滾 */ @RequestMapping("/a4") public void a4(){ transactionalService.a4(); } /** * 同類中在方法a中調用b * a有事務,b也有 ,異常發生在b中 會回滾 */ @RequestMapping("/a5") public void a5(){ transactionalService.a5(); } /** * 同類中在方法a中調用b * a有事務,b也有 ,異常發生在a中 會回滾 */ @RequestMapping("/a6") public void a6(){ transactionalService.a6(); } /** *a類中調用b類中的方法 * a中有事務,b中也有 會回滾 * */ @RequestMapping("/b5") public void b5(){ transactionalService.b5(); } /** *a類中調用b類中的方法 * a中有事務,b中沒有 會回滾 * */ @RequestMapping("/b6") public void b6(){ transactionalService.b6(); } /** *a類中調用b類中的方法 * a沒有事務,b中有 不會回滾 * */ @RequestMapping("/b7") public void b7(){ transactionalService.b7(); } /** *a類中調用b類中的方法 * a沒有事務,b中沒有 不會回滾 * */ @RequestMapping("/b8") public void b8(){ transactionalService.b8(); }
如果在a方法中調用b方法不管是不是a和b是不是在同一個類中,只要a方法中沒有事務,則發生異常的時候不會回滾,即:當a無事務時,則a和b均沒有事務,當a有事務時,b如果有事務,則b事務會加到a事務中,二者為同一事務!
感謝各位的閱讀!關于“如何解決spring聲明式事務@Transactional不回滾的多種情況問題”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。