轉(zhuǎn)自【一起來(lái)學(xué)SpringBoot(八)事務(wù)的控制 - fulinlin的博客 - CSDN博客】https://blog.csdn.net/qq_32867467/article/details/82944473
編程式事務(wù)
編程式事務(wù)管理使用TransactionTemplate或者直接使用底層的PlatformTransactionManager屁魏。對(duì)于編程式事務(wù)管理,spring推薦使用TransactionTemplate。
聲明式事務(wù)
編程式事務(wù)每次實(shí)現(xiàn)都要單獨(dú)實(shí)現(xiàn)慈格,但業(yè)務(wù)量大功能復(fù)雜時(shí)腾供,使用編程式事務(wù)無(wú)疑是痛苦的炕泳,而聲明式事務(wù)不同将宪,聲明式事務(wù)屬于無(wú)侵入式胧谈,不會(huì)影響業(yè)務(wù)邏輯的實(shí)現(xiàn)过椎。
聲明式事務(wù)管理使用了 AOP 實(shí)現(xiàn)的室梅,本質(zhì)就是在目標(biāo)方法執(zhí)行前后進(jìn)行攔截。在目標(biāo)方法執(zhí)行前加入或創(chuàng)建一個(gè)事務(wù),在執(zhí)行方法執(zhí)行后亡鼠,根據(jù)實(shí)際情況選擇提交或是回滾事務(wù)赏殃。
@Transactional可以作用于接口、接口方法拆宛、類以及類方法上嗓奢。當(dāng)作用于類上時(shí),該類的所有 public 方法將都具有該類型的事務(wù)屬性浑厚,同時(shí)股耽,我們也可以在方法級(jí)別使用該標(biāo)注來(lái)覆蓋類級(jí)別的定義。因此可以在Service層和Controller層使用钳幅。
在此處需要特別注意的是物蝙,此@Transactional注解來(lái)自org.springframework.transaction.annotation包,而不是javax.transaction敢艰。
這里特意說(shuō)明一下
1.如果在接口诬乞、實(shí)現(xiàn)類或方法上都指定了@Transactional注解,則優(yōu)先級(jí)順序?yàn)榉椒?gt;實(shí)現(xiàn)類>接口钠导;
2.建議只在實(shí)現(xiàn)類或?qū)崿F(xiàn)類的方法上使用@Transactional震嫉,而不要在接口上使用,這是因?yàn)槿绻褂肑DK代理機(jī)制(基于接口的代理)是沒(méi)問(wèn)題牡属;而使用使用CGLIB代理(繼承)機(jī)制時(shí)就會(huì)遇到問(wèn)題票堵,因?yàn)槠涫褂没陬惖拇矶皇墙涌冢@是因?yàn)榻涌谏系腀Transactional注解是“不能繼承的”逮栅;
3.Spring提供了一個(gè)@EnableTransactionManagement注解在配置類上來(lái)開(kāi)啟聲明式事務(wù)的支持悴势。使用了@EnableTransactionManagement后,Spring容器會(huì)自動(dòng)掃描注解@Transactional的方法和類措伐。
@Transactional參數(shù)功能名稱描述:
readOnly 該屬性用于設(shè)置當(dāng)前事務(wù)是否為只讀事務(wù)特纤,設(shè)置為true表示只讀,false則表示可讀寫(xiě)侥加,默認(rèn)值為false捧存。例如:@Transactional(readOnly=true)
rollbackFor 該屬性用于設(shè)置需要進(jìn)行回滾的異常類數(shù)組,當(dāng)方法中拋出指定異常數(shù)組中的異常時(shí)担败,則進(jìn)行事務(wù)回滾矗蕊。例如:指定單一異常類:@Transactional(rollbackFor=RuntimeException.class)指定多個(gè)異常類:@Transactional(rollbackFor={RuntimeException.class, Exception.class})
rollbackForClassName 該屬性用于設(shè)置需要進(jìn)行回滾的異常類名稱數(shù)組,當(dāng)方法中拋出指定異常名稱數(shù)組中的異常時(shí)氢架,則進(jìn)行事務(wù)回滾。例如:指定單一異常類名稱@Transactional(rollbackForClassName=”RuntimeException”)指定多個(gè)異常類名稱:@Transactional(rollbackForClassName={“RuntimeException”,”Exception”})
noRollbackFor 該屬性用于設(shè)置不需要進(jìn)行回滾的異常類數(shù)組朋魔,當(dāng)方法中拋出指定異常數(shù)組中的異常時(shí)岖研,不進(jìn)行事務(wù)回滾。例如:指定單一異常類:@Transactional(noRollbackFor=RuntimeException.class)指定多個(gè)異常類:@Transactional(noRollbackFor={RuntimeException.class, Exception.class})
noRollbackForClassName 該屬性用于設(shè)置不需要進(jìn)行回滾的異常類名稱數(shù)組,當(dāng)方法中拋出指定異常名稱數(shù)組中的異常時(shí)孙援,不進(jìn)行事務(wù)回滾害淤。例如:指定單一異常類名稱:@Transactional(noRollbackForClassName=”RuntimeException”)指定多個(gè)異常類名稱:@Transactional(noRollbackForClassName={“RuntimeException”,”Exception”})
propagation 該屬性用于設(shè)置事務(wù)的傳播行為。例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
isolation 該屬性用于設(shè)置底層數(shù)據(jù)庫(kù)的事務(wù)隔離級(jí)別拓售,事務(wù)隔離級(jí)別用于處理多事務(wù)并發(fā)的情況窥摄,通常使用數(shù)據(jù)庫(kù)的默認(rèn)隔離級(jí)別即可,基本不需要進(jìn)行設(shè)置
timeout 該屬性用于設(shè)置事務(wù)的超時(shí)秒數(shù)础淤,默認(rèn)值為-1表示永不超時(shí)
事務(wù)屬性
事務(wù)屬性包含以下五個(gè)方面:隔離級(jí)別崭放、傳播行為、回滾規(guī)則鸽凶、事務(wù)超時(shí)币砂、只讀。
注意點(diǎn)
僅對(duì) public 方法有效
只有 @Transactional 注解應(yīng)用到 public 方法上才能進(jìn)行事務(wù)管理玻侥。這是因?yàn)?Spring 在 AOP 事務(wù)注解時(shí)决摧,在讀取注解上的屬性方法中,會(huì)優(yōu)先判斷方法是否是 public凑兰,如果不是 public掌桩,就不會(huì)讀取事務(wù)配置信息。
AOP 的自調(diào)用問(wèn)題
在 Spring 的 AOP 代理下姑食,只有目標(biāo)方法由外部調(diào)用波岛,目標(biāo)方法才由 Spring 生成的代理對(duì)象來(lái)管理。也就是說(shuō)矢门,在同一個(gè)類中的一個(gè) @Transactional 方法中盆色,去調(diào)用另一個(gè) @Transactional 方法,會(huì)導(dǎo)致第二個(gè)方法的事務(wù)無(wú)效祟剔,被 Spring AOP 所忽略隔躲。
對(duì)方法try-catch
對(duì)方法進(jìn)行try-catch后 捕捉異常,則事物就失效了物延,如果既想try-catch又想事物回歸怎么辦呢宣旱?這樣就行了。
多個(gè)事務(wù)管理器的處理
關(guān)于事務(wù)管理器叛薯,不管是JPA還是JDBC等都實(shí)現(xiàn)自接口 PlatformTransactionManager浑吟。 如果你添加的是 spring-boot-starter-jdbc 依賴,框架會(huì)默認(rèn)注入 DataSourceTransactionManager 實(shí)例耗溜。如果你添加的是 spring-boot-starter-data-jpa 依賴组力,框架會(huì)默認(rèn)注入 JpaTransactionManager 實(shí)例。
這些SpringBoot為我們自動(dòng)做了抖拴,對(duì)我們并不透明燎字。如果你項(xiàng)目做的比較大腥椒,添加的持久化依賴比較多,我們還是會(huì)選擇人為的指定使用哪個(gè)事務(wù)管理器候衍。代碼如下:
@EnableTransactionManagement
@SpringBootApplication
public class ProfiledemoApplication {
// 其中 dataSource 框架會(huì)自動(dòng)為我們注入
@Bean
public PlatformTransactionManager txManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public Object testBean(PlatformTransactionManager platformTransactionManager) {
System.out.println(">>>>>>>>>>" + platformTransactionManager.getClass().getName());
return new Object();
}
public static void main(String[] args) {
SpringApplication.run(ProfiledemoApplication.class, args);
}
}
在Spring容器中笼蛛,我們手工注解@Bean 將被優(yōu)先加載,框架不會(huì)重新實(shí)例化其他的 PlatformTransactionManager 實(shí)現(xiàn)類蛉鹿。
然后在Service中滨砍,被 @Transactional 注解的方法,將支持事務(wù)妖异。如果注解在類上惋戏,則整個(gè)類的所有方法都默認(rèn)支持事務(wù)。
對(duì)于同一個(gè)工程中存在多個(gè)事務(wù)管理器要怎么處理随闺,請(qǐng)看下面的實(shí)例日川,具體說(shuō)明請(qǐng)看代碼中的注釋。
@EnableTransactionManagement// 開(kāi)啟注解事務(wù)管理矩乐,等同于xml配置文件中的 <tx:annotation-driven />
@SpringBootApplication
public class ProfiledemoApplication implements TransactionManagementConfigurer {
@Resource(name="txManager2")
private PlatformTransactionManager txManager2;
// 創(chuàng)建事務(wù)管理器1
@Bean(name = "txManager1")
public PlatformTransactionManager txManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
// 創(chuàng)建事務(wù)管理器2
@Bean(name = "txManager2")
public PlatformTransactionManager txManager2(EntityManagerFactory factory) {
return new JpaTransactionManager(factory);
}
// 實(shí)現(xiàn)接口TransactionManagementConfigurer 方法龄句,其返回值代表在擁有多個(gè)事務(wù)管理器的情況下默認(rèn)使用的事務(wù)管理器
@Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return txManager2;
}
public static void main(String[] args) {
SpringApplication.run(ProfiledemoApplication.class, args);
}
}
@Component
public class DevSendMessage implements SendMessage {
// 使用value具體指定使用哪個(gè)事務(wù)管理器
@Transactional(value="txManager1")
@Override
public void send() {
System.out.println(">>>>>>>>Dev Send()<<<<<<<<");
send2();
}
// 在存在多個(gè)事務(wù)管理器的情況下,如果沒(méi)有使用value具體指定散罕,則默認(rèn)使用方法? annotationDrivenTransactionManager() 返回的事務(wù)管理器
@Transactional
public void send2() {
System.out.println(">>>>>>>>Dev Send2()<<<<<<<<");
}
}
注:如果Spring容器中存在多個(gè) PlatformTransactionManager 實(shí)例分歇,并且沒(méi)有實(shí)現(xiàn)接口 TransactionManagementConfigurer 指定默認(rèn)值,在我們?cè)诜椒ㄉ鲜褂米⒔?@Transactional 的時(shí)候欧漱,就必須要用value指定职抡,如果不指定,則會(huì)拋出異常误甚。
對(duì)于系統(tǒng)需要提供默認(rèn)事務(wù)管理的情況下缚甩,實(shí)現(xiàn)接口TransactionManagementConfigurer 指定。
對(duì)有的系統(tǒng)窑邦,為了避免不必要的問(wèn)題擅威,在業(yè)務(wù)中必須要明確指定 @Transactional 的 value 值的情況下。不建議實(shí)現(xiàn)接口 TransactionManagementConfigurer冈钦,這樣控制臺(tái)會(huì)明確拋出異常郊丛,開(kāi)發(fā)人員就不會(huì)忘記主動(dòng)指定。