您好,登錄后才能下訂單哦!
本篇內容介紹了“怎么解決數據庫事務居然沒生效問題”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
Spring聲明式事務提供給 Javaer 們方便的事務配置方式,再搭配Spring Boot自動配置,基本只需在方法上添加@Transactional注解,即可瞬間開啟方法的事務性配置。
但僅為方法添加@Transactional注解
你就以為這就夠了嗎?
事務未被正確處理,一般不會導致停止服務,更不易在測試階段復現。但隨系統業務越來越復雜,就會帶來大量數據不一致問題,隨后就是大量線上問題而后人工排查檢修數據。
1 你的Spring事務怎么才算生效?
使用@Transactional開啟聲明式事務時, 靈魂發問:事務生效了嗎?
案例
用戶表實體類
DAO 層
根據username查詢所有數據
@Repository public interface UserRepository extends JpaRepository<UserEntity, Long> { List<UserEntity> findByName(String name); }
Service層
UserService類
負責業務邏輯處理,包括如下方法:
createUserWrong1調用private方法:
createUserPrivate,被@Transactional注解。當傳入的用戶名包含test則拋異常,讓用戶的創建操作失敗,期望事務回滾:
getUserCount
Controller層
調用一下剛才定義的UserService中的入口方法createUserWrong1。
測試結果
即便用戶名不合法,用戶也能創建成功。刷新瀏覽器,多次發現非法用戶注冊。
2 @Transactional怎么確保生效?
除非特殊配置(比如使用AspectJ靜態織入實現AOP),否則只有定義在public方法上的@Transactional才能生效。
Spring默認通過動態代理實現AOP,對目標方法增強,private方法無法代理到,自然也無法動態增強事務處理邏輯。
那簡單,把createUserPrivate方法改為public即可。
在UserService中再建一個入口方法createUserWrong2,來調用這個public方法再次嘗試:
public int createUserWrong2(String name) { try { this.createUserPublic(new UserEntity(name)); } catch (Exception ex) { log.error("create user failed because {}", ex.getMessage()); } return userRepository.findByName(name).size(); } //標記了@Transactional的public方法 @Transactional public void createUserPublic(UserEntity entity) { userRepository.save(entity); if (entity.getName().contains("test")) throw new RuntimeException("invalid username!"); }
新的createUserWrong2方法事務同樣不生效。
必須通過代理過的類從外部調用目標方法
要調用增強過的方法必然是調用代理后的對象。
嘗試修改UserService,注入一個self,然后再通過self實例調用標記有@Transactional注解的createUserPublic方法。設置斷點可以看到,self是由Spring通過CGLIB方式增強過的類。
CGLIB通過繼承方式實現代理類,private方法在子類不可見,自然也就無法進行事務增強;
this指針代表對象自己,Spring不可能注入this,所以通過this訪問方法必然不是代理。
把this改為self,在Controller中調用createUserRight方法可以驗證事務生效了:非法的用戶注冊操作可以回滾。
雖然在UserService內部注入自己調用自己的createUserPublic可以正確實現事務,但這不符合習慣用法。更合理的實現方式是,讓Controller直接調用之前定義的UserService的createUserPublic方法。
@GetMapping("right2") public int right2(@RequestParam("name") String name) { try { userService.createUserPublic(new UserEntity(name)); } catch (Exception ex) { log.error("create user failed because {}", ex.getMessage()); } return userService.getUserCount(name); }
this自調用/self調用/Controller調用UserService
this自調用
無法走到Spring代理類
后兩種
調用的Spring注入的UserService,通過代理調用才有機會對createUserPublic方法進行動態增強。
推薦在開發時打開相關Debug日志,以了解Spring事務實現的細節。
比如JPA數據庫訪問,可以這么開啟Debug日志:
logging.level.org.springframework.orm.jpa=DEBUG
開啟日志后再比較下在UserService中this調用、Controller中通過注入的UserService Bean調用createUserPublic的區別。
很明顯,this調用因沒走代理,事務沒有在createUserPublic生效,只在Repository的save生效:
// 在UserService中通過this調用public的createUserPublic [23:04:30.748] [http-nio-45678-exec-5] [DEBUG] [o.s.orm.jpa.JpaTransactionManager:370 ] - Creating new transaction with name [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT [DEBUG] [o.s.orm.jpa.JpaTransactionManager :370 ] - Creating new transaction with name [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT //在Controller中通過注入的UserService Bean調用createUserPublic [10:10:47.750] [http-nio-45678-exec-6] [DEBUG] [o.s.orm.jpa.JpaTransactionManager :370 ] - Creating new transaction with name [org.geekbang.time.commonmistakes.transaction.demo1.UserService.createUserPublic]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
這種實現在Controller里處理異常顯得繁瑣,還不如直接把createUserWrong2加@Transactional注解,然后在Controller中直接調用該方法。
“怎么解決數據庫事務居然沒生效問題”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。