自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

線上問題事跡(一)數(shù)據(jù)庫事務(wù)居然都沒生效?

數(shù)據(jù)庫
Spring聲明式事務(wù)提供給 Javaer 們方便的事務(wù)配置方式,再搭配Spring Boot自動(dòng)配置,基本只需在方法上添加@Transactional注解,即可瞬間開啟方法的事務(wù)性配置。

[[352574]]

Spring聲明式事務(wù)提供給 Javaer 們方便的事務(wù)配置方式,再搭配Spring Boot自動(dòng)配置,基本只需在方法上添加@Transactional注解,即可瞬間開啟方法的事務(wù)性配置。

  • 但僅為方法添加@Transactional注解

你就以為這就夠了嗎?

事務(wù)未被正確處理,一般不會導(dǎo)致停止服務(wù),更不易在測試階段復(fù)現(xiàn)。但隨系統(tǒng)業(yè)務(wù)越來越復(fù)雜,就會帶來大量數(shù)據(jù)不一致問題,隨后就是大量線上問題而后人工排查檢修數(shù)據(jù)。

1 你的Spring事務(wù)怎么才算生效?

使用@Transactional開啟聲明式事務(wù)時(shí), 靈魂發(fā)問:事務(wù)生效了嗎?

案例

用戶表實(shí)體類

 

DAO 層 

根據(jù)username查詢所有數(shù)據(jù)

  1. @Repository 
  2. public interface UserRepository extends JpaRepository<UserEntity, Long> { 
  3.     List<UserEntity> findByName(String name); 

Service層

UserService類

負(fù)責(zé)業(yè)務(wù)邏輯處理,包括如下方法:

createUserWrong1調(diào)用private方法:

createUserPrivate,被@Transactional注解。當(dāng)傳入的用戶名包含test則拋異常,讓用戶的創(chuàng)建操作失敗,期望事務(wù)回滾: 


getUserCount

 

Controller層 

調(diào)用一下剛才定義的UserService中的入口方法createUserWrong1。 


測試結(jié)果

即便用戶名不合法,用戶也能創(chuàng)建成功。刷新瀏覽器,多次發(fā)現(xiàn)非法用戶注冊。

2 @Transactional怎么確保生效?

除非特殊配置(比如使用AspectJ靜態(tài)織入實(shí)現(xiàn)AOP),否則只有定義在public方法上的@Transactional才能生效。

Spring默認(rèn)通過動(dòng)態(tài)代理實(shí)現(xiàn)AOP,對目標(biāo)方法增強(qiáng),private方法無法代理到,自然也無法動(dòng)態(tài)增強(qiáng)事務(wù)處理邏輯。

那簡單,把createUserPrivate方法改為public即可。

在UserService中再建一個(gè)入口方法createUserWrong2,來調(diào)用這個(gè)public方法再次嘗試:

  1. public int createUserWrong2(String name) { 
  2.     try { 
  3.         this.createUserPublic(new UserEntity(name)); 
  4.     } catch (Exception ex) { 
  5.         log.error("create user failed because {}", ex.getMessage()); 
  6.     } 
  7.     return userRepository.findByName(name).size(); 
  8.  
  9. //標(biāo)記了@Transactional的public方法 
  10. @Transactional 
  11. public void createUserPublic(UserEntity entity) { 
  12.     userRepository.save(entity); 
  13.     if (entity.getName().contains("test")) 
  14.         throw new RuntimeException("invalid username!"); 

新的createUserWrong2方法事務(wù)同樣不生效。

必須通過代理過的類從外部調(diào)用目標(biāo)方法

要調(diào)用增強(qiáng)過的方法必然是調(diào)用代理后的對象。

嘗試修改UserService,注入一個(gè)self,然后再通過self實(shí)例調(diào)用標(biāo)記有@Transactional注解的createUserPublic方法。設(shè)置斷點(diǎn)可以看到,self是由Spring通過CGLIB方式增強(qiáng)過的類。

CGLIB通過繼承方式實(shí)現(xiàn)代理類,private方法在子類不可見,自然也就無法進(jìn)行事務(wù)增強(qiáng);

this指針代表對象自己,Spring不可能注入this,所以通過this訪問方法必然不是代理。

把this改為self,在Controller中調(diào)用createUserRight方法可以驗(yàn)證事務(wù)生效了:非法的用戶注冊操作可以回滾。

雖然在UserService內(nèi)部注入自己調(diào)用自己的createUserPublic可以正確實(shí)現(xiàn)事務(wù),但這不符合習(xí)慣用法。更合理的實(shí)現(xiàn)方式是,讓Controller直接調(diào)用之前定義的UserService的createUserPublic方法。

  1. @GetMapping("right2"
  2. public int right2(@RequestParam("name") String name) { 
  3.     try { 
  4.         userService.createUserPublic(new UserEntity(name)); 
  5.     } catch (Exception ex) { 
  6.         log.error("create user failed because {}", ex.getMessage()); 
  7.     } 
  8.     return userService.getUserCount(name); 

this自調(diào)用/self調(diào)用/Controller調(diào)用UserService


  • this自調(diào)用

          無法走到Spring代理類

  • 后兩種

          調(diào)用的Spring注入的UserService,通過代理調(diào)用才有機(jī)會對createUserPublic方法進(jìn)行動(dòng)態(tài)增強(qiáng)。

推薦在開發(fā)時(shí)打開相關(guān)Debug日志,以了解Spring事務(wù)實(shí)現(xiàn)的細(xì)節(jié)。

比如JPA數(shù)據(jù)庫訪問,可以這么開啟Debug日志:

logging.level.org.springframework.orm.jpa=DEBUG

開啟日志后再比較下在UserService中this調(diào)用、Controller中通過注入的UserService Bean調(diào)用createUserPublic的區(qū)別。

很明顯,this調(diào)用因沒走代理,事務(wù)沒有在createUserPublic生效,只在Repository的save生效:

  1. // 在UserService中通過this調(diào)用public的createUserPublic 
  2. [23:04:30.748] [http-nio-45678-exec-5] [DEBUG] [o.s.orm.jpa.JpaTransactionManager:370 ] -  
  3. Creating new transaction with name [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]:  
  4. PROPAGATION_REQUIRED,ISOLATION_DEFAULT 
  5.  
  6. [DEBUG] [o.s.orm.jpa.JpaTransactionManager       :370 ] - Creating new transaction with name [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT 
  7. //在Controller中通過注入的UserService Bean調(diào)用createUserPublic 
  8. [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 

這種實(shí)現(xiàn)在Controller里處理異常顯得繁瑣,還不如直接把createUserWrong2加@Transactional注解,然后在Controller中直接調(diào)用該方法。

這既能從外部(Controller中)調(diào)用UserService方法,方法又是public的能夠被動(dòng)態(tài)代理AOP增強(qiáng)。要不你試試?看看效果如何,下回分解~

 

責(zé)任編輯:姜華 來源: JavaEdge
相關(guān)推薦

2020-11-18 10:16:52

數(shù)據(jù)庫回滾事務(wù)

2020-11-18 08:32:07

數(shù)據(jù)庫

2019-04-15 13:15:12

數(shù)據(jù)庫MySQL死鎖

2010-10-08 09:38:55

Android數(shù)據(jù)庫事

2009-09-24 14:12:22

Hibernate數(shù)據(jù)

2024-06-21 09:37:57

2025-04-08 06:00:00

2024-05-28 00:00:30

Golang數(shù)據(jù)庫

2020-06-17 16:56:36

數(shù)據(jù)庫MySQL跨行事務(wù)

2017-08-22 17:10:45

數(shù)據(jù)庫MySQL事務(wù)模型

2019-05-13 08:24:58

數(shù)據(jù)庫MySQLInnoDB

2021-10-03 15:00:44

數(shù)據(jù)庫mysql單機(jī)

2020-08-20 07:37:21

數(shù)據(jù)庫開源框架

2010-05-31 15:12:44

MySQL數(shù)據(jù)庫

2018-09-06 14:53:39

數(shù)據(jù)庫事務(wù)隔離隔離級別

2009-08-06 18:10:06

C#數(shù)據(jù)庫事務(wù)

2011-08-12 13:33:31

Oracle數(shù)據(jù)庫自治事務(wù)

2018-07-20 11:10:21

數(shù)據(jù)庫事務(wù)隔離性

2023-10-11 08:09:53

事務(wù)隔離級別

2011-08-15 11:24:46

SQL Server事務(wù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號