詳細(xì)談?wù)凷pring事務(wù)是如何管理的
前言
我們都知道Spring給我們提供了很多抽象,比如我們?cè)诓僮鲾?shù)據(jù)庫(kù)的過(guò)程中,它為我們提供了事務(wù)方面的抽象,讓我們可以非常方便的以事務(wù)方式操作數(shù)據(jù)庫(kù)。不管你用JDBC、Mybatis、Hibernate等任何一種方式操作數(shù)據(jù)庫(kù),也不管你使用DataSource還是JTA的事務(wù),Spring事務(wù)抽象管理都能很好的把他統(tǒng)一在一起。接下來(lái)看一下事務(wù)的抽象核心接口
Spring事務(wù)抽象
PlatformTransactionManager是事務(wù)管理器接口
//事務(wù)管理器接口有以下幾個(gè)接口,獲取事務(wù)信息,提交和回滾
public interface PlatformTransactionManager {
TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
void commit(TransactionStatus var1) throws TransactionException;
void rollback(TransactionStatus var1) throws TransactionException;
}
常見(jiàn)的事務(wù)管理器有以下幾種
- DataSourceTransactionManager
- HibernateTransactionManager
- JtaTransactionManager
這些管理器都實(shí)現(xiàn)了PlatformTransactionManager中的三個(gè)接口,實(shí)現(xiàn)邏輯略有差別,但是對(duì)用戶來(lái)講區(qū)別不大
定義事務(wù)的一些參數(shù):
一些事務(wù)的參數(shù)在TransactionDefinition.java中,詳情如下:
public interface TransactionDefinition {
int PROPAGATION_REQUIRED = 0;
int PROPAGATION_SUPPORTS = 1;
int PROPAGATION_MANDATORY = 2;
int PROPAGATION_REQUIRES_NEW = 3;
int PROPAGATION_NOT_SUPPORTED = 4;
int PROPAGATION_NEVER = 5;
int PROPAGATION_NESTED = 6;
//默認(rèn)隔離級(jí)別,和數(shù)據(jù)庫(kù)的隔離級(jí)別一致
int ISOLATION_DEFAULT = -1;
int ISOLATION_READ_UNCOMMITTED = 1;
int ISOLATION_READ_COMMITTED = 2;
int ISOLATION_REPEATABLE_READ = 4;
int ISOLATION_SERIALIZABLE = 8;
//默認(rèn)不超時(shí)
int TIMEOUT_DEFAULT = -1;
}
下面兩張圖對(duì)這些參數(shù)進(jìn)行了說(shuō)明:
7種事務(wù)傳播特性:

四種事務(wù)隔離級(jí)別:
在看事務(wù)隔離級(jí)別前需要先了解下什么是臟讀、不可重復(fù)讀、幻讀
臟讀: 臟讀就是一個(gè)事物未提交的數(shù)據(jù),被另外一個(gè)事務(wù)讀到了,顯然這種情況不可接受
不可重復(fù)讀: 不可重復(fù)讀是指在一個(gè)事務(wù)內(nèi),多次讀同一數(shù)據(jù),前后讀取的結(jié)果不一致。
幻讀: 事務(wù)A對(duì)表中的一個(gè)數(shù)據(jù)進(jìn)行了修改,這種修改涉及到表中的全部數(shù)據(jù)行。同時(shí)事務(wù)B也修改了這個(gè)表中的數(shù)據(jù),這種修改是向表中插入一行新數(shù)據(jù)。那么就會(huì)發(fā)生操作事務(wù)A的用戶發(fā)現(xiàn)表中還存在沒(méi)有修改的數(shù)據(jù)行,就好像發(fā)生了幻覺(jué)一樣
知道了以上幾個(gè)概念,我們來(lái)看看隔離級(jí)別:

這里我們可以看到,Spring并不是提供了所有的事務(wù)管理的實(shí)現(xiàn),而是提供了標(biāo)準(zhǔn)的事務(wù)管理器的操作接口PlatformTransactionManager, 并且規(guī)范了其行為,具體的事務(wù)實(shí)現(xiàn)由各個(gè)平臺(tái)自行實(shí)現(xiàn)。這就是Spring的事務(wù)抽象。
Spring之編程式事務(wù)
Spring提供了TransactionTemplate工具類可以很方便的使用編程式事務(wù)。默認(rèn)情況下TransactionTemplate使用的是DataSourceTransactionManager。
在Spring上下文中,我們不配置TransactionTemplate這個(gè)bean,也能獲取到TransactionTemplate。比如下面的例子。
@Service
public class UserInfoService {
@Resource
private UserInfoDAO userInfoDAO;
@Autowired
private TransactionTemplate transactionTemplate;
public void updateUser1(){
transactionTemplate.execute(transactionStatus -> {
userInfoDAO.updateUserName(1,"zhangsanfeng");
transactionTemplate.execute(transactionStatus2 -> {
userInfoDAO.updateUserName(2,"lisi");
return null;
});
return null;
});
}
}
由于Spring默認(rèn)的事務(wù)傳播特性是PROPAGATION_REQUIRED,我們來(lái)做一下驗(yàn)證,看是不是這樣


上面兩幅圖可以看出,TransactionStatus中的newTransaction屬性,第一個(gè)是true,第二個(gè)是false,正好符合PROPAGATION_REQUIRED所描述的情況。其他的傳播特性可以自己去驗(yàn)證。
聲明式事務(wù)
除了編程式事務(wù)外,Spring還為我們提供了聲明式事務(wù)。使用@Transactional注解。
@Transactional 可以作用于接口、接口方法、類以及類方法上。當(dāng)作用于類上時(shí),該類的所有 public 方法將都具有該類型的事務(wù)屬性,同時(shí),我們也可以在方法級(jí)別使用該注解來(lái)覆蓋類級(jí)別的定義。
雖然 @Transactional 注解可以作用于接口、接口方法、類以及類方法上,但是 Spring 建議不要在接口或者接口方法上使用該注解,因?yàn)檫@只有在使用基于接口的代理時(shí)它才會(huì)生效。另外, @Transactional 注解應(yīng)該只被應(yīng)用到 public 方法上,這是由 Spring AOP 的本質(zhì)決定的。如果你在 protected、private 或者默認(rèn)可見(jiàn)性的方法上使用 @Transactional 注解,這將被忽略,也不會(huì)拋出任何異常。
@Transactional的rollbackFor屬性可以設(shè)置一個(gè) Throwable 的數(shù)組,用來(lái)表明如果方法拋出這些異常,則進(jìn)行事務(wù)回滾。默認(rèn)情況下如果不配置rollbackFor屬性,那么事務(wù)只會(huì)在遇到RuntimeException的時(shí)候才會(huì)回滾。
下面的代碼事務(wù)就不會(huì)生效:
@Transactional
public void updateUser2() throws Exception {
int r1 = userInfoDAO.updateUserName(1,"wanger");
int r2 = userInfoDAO.updateUserName(2,"mawu");
if (r2==1){
throw new Exception();
}
}
如果我們把拋出的異常改成RuntimeException,這時(shí)候事務(wù)就會(huì)生效了。或者指定異常讓事務(wù)生效,比如 @Transactional(rollbackFor = Exception.class),這樣碰到所有的異常事務(wù)都會(huì)生效了。
為什么加了@Transactional注解事物就生效了?
這是因?yàn)镾pring容器會(huì)為加了這個(gè)注解的對(duì)象生成一個(gè)代理對(duì)象,實(shí)際調(diào)用的時(shí)候,實(shí)際上是調(diào)用的代理對(duì)象。 代理對(duì)象的實(shí)現(xiàn)了AOP的增強(qiáng),實(shí)現(xiàn)了事務(wù)的實(shí)現(xiàn)。

通過(guò)注解怎么實(shí)現(xiàn)指定的傳播特性和隔離級(jí)別的?
public @interface Transactional {
@AliasFor("transactionManager")
String value() default "";
@AliasFor("value")
String transactionManager() default "";
String[] label() default {};
Propagation propagation() default Propagation.REQUIRED;
Isolation isolation() default Isolation.DEFAULT;
int timeout() default -1;
String timeoutString() default "";
boolean readOnly() default false;
Class<? extends Throwable>[] rollbackFor() default {};
String[] rollbackForClassName() default {};
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
}
代碼中可以看出,我們可以指定隔離級(jí)別和傳播特性,在Spring為我們生成代理類的時(shí)候,會(huì)讀取這些屬性,體現(xiàn)在增強(qiáng)邏輯中。
事務(wù)失效的8種情況及解決辦法
數(shù)據(jù)庫(kù)引擎不支持事務(wù)
這里以 MySQL 為例,其 MyISAM 引擎是不支持事務(wù)操作的,InnoDB 才是支持事務(wù)的引擎,一般要支持事務(wù)都會(huì)使用 InnoDB,這時(shí)候選擇支持事務(wù)的數(shù)據(jù)庫(kù)即可(好像是廢話,哈哈哈)
沒(méi)有被 Spring 管理
這個(gè)好像沒(méi)什么可說(shuō)的,脫離了Spring的管理,還談什么Spring事務(wù)管理。
方法不是 public 的
@Transactional 只能用于 public 的方法上,否則事務(wù)不會(huì)失效,如果要用在非 public 方法上,可以開(kāi)啟 AspectJ 代理模式。
數(shù)據(jù)源沒(méi)有配置事務(wù)管理器
相當(dāng)于沒(méi)開(kāi)啟事務(wù)管理,如果不是Springboot情況需要進(jìn)行如下操作。
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
如果是SpringBoot,在啟動(dòng)類上直接加上注解@EnableTransactionManagement即可。
傳播特性配錯(cuò)了
傳播特性配置成,Propagation.SUPPORTED或者Propagation.NOT_SUPPORTED,改成支持事務(wù)的傳播特性即可。
異常類型錯(cuò)誤
因?yàn)槟J(rèn)的異常類型是運(yùn)行時(shí)異常,如果拋出了其他異常就不生效。
解決方式:
1、將異常改成運(yùn)行時(shí)異常
2、指定異常進(jìn)行事務(wù)回滾,如:@Transactional(rollbackFor = Exception.class)
異常被吃掉了
如果你代碼這么寫(xiě),事務(wù)不生效:
@Transactional(rollbackFor = Exception.class)
public void updateUser2() {
int r1 = userInfoDAO.updateUserName(1,"3");
int r2 = userInfoDAO.updateUserName(2,"4");
if (r2==1){
throw new RuntimeException();
}
try {
}catch (Exception e){
}
}
解決辦法: 必須要拋出異常,否則Spring事務(wù)管理,不會(huì)走到回滾邏輯
類內(nèi)部調(diào)用
@Service
public class UserInfoService {
public void justUpdate(){
updateUser2();
}
@Transactional(rollbackFor = Exception.class)
public void updateUser2() {
}
}
上述代碼不生效,因?yàn)閮?nèi)部調(diào)用不會(huì)涉及到代理類的調(diào)用,更不會(huì)有AOP的增強(qiáng),因此不會(huì)生效。
解決辦法:
1、自注入
@Service
public class UserInfoService {
@Autowired
private UserInfoService userInfoService;
public void justUpdate(){
userInfoService.updateUser2();
}
@Transactional(rollbackFor = Exception.class)
public void updateUser2() {
}
}
2、Spring上下文
@Service
public class UserInfoService {
ApplicationContext applicationContext;
public void justUpdate(){
UserInfoService userInfoService = (UserInfoService) applicationContext.getBean("userInfoService");
userInfoService.updateUser2();
}
@Transactional(rollbackFor = Exception.class)
public void updateUser2() {
}
}
3、獲取他的代理類,直接調(diào)用代理類
@Service
public class UserInfoService {
public void justUpdate(){
((UserInfoService) AopContext.currentProxy()).updateUser2();
}
@Transactional(rollbackFor = Exception.class)
public void updateUser2() {
}
}
總結(jié)
到此這篇關(guān)于Spring事務(wù)是如何管理的的文章就介紹到這了,更多相關(guān)Spring事務(wù)管理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(41)
下面小編就為大家?guī)?lái)一篇Java基礎(chǔ)的幾道練習(xí)題(分享)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧,希望可以幫到你2021-07-07
java 使用簡(jiǎn)單的demo實(shí)例告訴你優(yōu)化算法的強(qiáng)大
本篇文章介紹了,在java中使用簡(jiǎn)單的demo實(shí)例告訴你優(yōu)化算法的強(qiáng)大。需要的朋友參考下2013-05-05
spring boot使用logback日志級(jí)別打印控制操作
這篇文章主要介紹了spring boot使用logback日志級(jí)別打印控制操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-03-03
Java中的 FilterInputStream簡(jiǎn)介_(kāi)動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
FilterInputStream 的作用是用來(lái)“封裝其它的輸入流,并為它們提供額外的功能”。接下來(lái)通過(guò)本文給大家分享Java中的 FilterInputStream簡(jiǎn)介,感興趣的朋友一起學(xué)習(xí)吧2017-05-05

