偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

線(xiàn)上問(wèn)題事跡(一)數(shù)據(jù)庫(kù)事務(wù)居然都沒(méi)生效?

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

[[352574]]

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

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

你就以為這就夠了嗎?

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

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

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

案例

用戶(hù)表實(shí)體類(lèi)

 

DAO 層 

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

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

Service層

UserService類(lèi)

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

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

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


getUserCount

 

Controller層 

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


測(cè)試結(jié)果

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

2 @Transactional怎么確保生效?

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

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

那簡(jiǎn)單,把createUserPrivate方法改為public即可。

在UserService中再建一個(gè)入口方法createUserWrong2,來(lái)調(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ù)同樣不生效。

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

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

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

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

this指針代表對(duì)象自己,Spring不可能注入this,所以通過(guò)this訪(fǎng)問(wèn)方法必然不是代理。

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

雖然在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)用

          無(wú)法走到Spring代理類(lèi)

  • 后兩種

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

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

比如JPA數(shù)據(jù)庫(kù)訪(fǎng)問(wèn),可以這么開(kāi)啟Debug日志:

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

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

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

  1. // 在UserService中通過(guò)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中通過(guò)注入的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é)任編輯:姜華 來(lái)源: JavaEdge
相關(guān)推薦

2020-11-18 10:16:52

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

2020-11-18 08:32:07

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

2019-04-15 13:15:12

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

2024-06-21 09:37:57

2010-10-08 09:38:55

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

2009-09-24 14:12:22

Hibernate數(shù)據(jù)

2025-04-08 06:00:00

2024-05-28 00:00:30

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

2020-06-17 16:56:36

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

2017-08-22 17:10:45

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

2019-05-13 08:24:58

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

2021-10-03 15:00:44

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

2020-08-20 07:37:21

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

2022-08-01 20:29:48

分布式架構(gòu)數(shù)據(jù)

2018-09-06 14:53:39

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

2010-05-31 15:12:44

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

2018-07-20 11:10:21

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

2009-08-06 18:10:06

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

2011-08-12 13:33:31

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

2023-10-11 08:09:53

事務(wù)隔離級(jí)別
點(diǎn)贊
收藏

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