詳細談談Spring事務是如何管理的
前言
我們都知道Spring給我們提供瞭很多抽象,比如我們在操作數據庫的過程中,它為我們提供瞭事務方面的抽象,讓我們可以非常方便的以事務方式操作數據庫。不管你用JDBC、Mybatis、Hibernate等任何一種方式操作數據庫,也不管你使用DataSource還是JTA的事務,Spring事務抽象管理都能很好的把他統一在一起。接下來看一下事務的抽象核心接口
Spring事務抽象
PlatformTransactionManager是事務管理器接口
//事務管理器接口有以下幾個接口,獲取事務信息,提交和回滾 public interface PlatformTransactionManager { TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException; void commit(TransactionStatus var1) throws TransactionException; void rollback(TransactionStatus var1) throws TransactionException; }
常見的事務管理器有以下幾種
- DataSourceTransactionManager
- HibernateTransactionManager
- JtaTransactionManager
這些管理器都實現瞭PlatformTransactionManager中的三個接口,實現邏輯略有差別,但是對用戶來講區別不大
定義事務的一些參數:
一些事務的參數在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; //默認隔離級別,和數據庫的隔離級別一致 int ISOLATION_DEFAULT = -1; int ISOLATION_READ_UNCOMMITTED = 1; int ISOLATION_READ_COMMITTED = 2; int ISOLATION_REPEATABLE_READ = 4; int ISOLATION_SERIALIZABLE = 8; //默認不超時 int TIMEOUT_DEFAULT = -1; }
下面兩張圖對這些參數進行瞭說明:
7種事務傳播特性:
四種事務隔離級別:
在看事務隔離級別前需要先瞭解下什麼是臟讀、不可重復讀、幻讀
臟讀: 臟讀就是一個事物未提交的數據,被另外一個事務讀到瞭,顯然這種情況不可接受
不可重復讀: 不可重復讀是指在一個事務內,多次讀同一數據,前後讀取的結果不一致。
幻讀: 事務A對表中的一個數據進行瞭修改,這種修改涉及到表中的全部數據行。同時事務B也修改瞭這個表中的數據,這種修改是向表中插入一行新數據。那麼就會發生操作事務A的用戶發現表中還存在沒有修改的數據行,就好像發生瞭幻覺一樣
知道瞭以上幾個概念,我們來看看隔離級別:
這裡我們可以看到,Spring並不是提供瞭所有的事務管理的實現,而是提供瞭標準的事務管理器的操作接口PlatformTransactionManager, 並且規范瞭其行為,具體的事務實現由各個平臺自行實現。這就是Spring的事務抽象。
Spring之編程式事務
Spring提供瞭TransactionTemplate工具類可以很方便的使用編程式事務。默認情況下TransactionTemplate使用的是DataSourceTransactionManager。
在Spring上下文中,我們不配置TransactionTemplate這個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默認的事務傳播特性是PROPAGATION_REQUIRED,我們來做一下驗證,看是不是這樣
上面兩幅圖可以看出,TransactionStatus中的newTransaction屬性,第一個是true,第二個是false,正好符合PROPAGATION_REQUIRED所描述的情況。其他的傳播特性可以自己去驗證。
聲明式事務
除瞭編程式事務外,Spring還為我們提供瞭聲明式事務。使用@Transactional註解。
@Transactional 可以作用於接口、接口方法、類以及類方法上。當作用於類上時,該類的所有 public 方法將都具有該類型的事務屬性,同時,我們也可以在方法級別使用該註解來覆蓋類級別的定義。
雖然 @Transactional 註解可以作用於接口、接口方法、類以及類方法上,但是 Spring 建議不要在接口或者接口方法上使用該註解,因為這隻有在使用基於接口的代理時它才會生效。另外, @Transactional 註解應該隻被應用到 public 方法上,這是由 Spring AOP 的本質決定的。如果你在 protected、private 或者默認可見性的方法上使用 @Transactional 註解,這將被忽略,也不會拋出任何異常。
@Transactional的rollbackFor屬性可以設置一個 Throwable 的數組,用來表明如果方法拋出這些異常,則進行事務回滾。默認情況下如果不配置rollbackFor屬性,那麼事務隻會在遇到RuntimeException的時候才會回滾。
下面的代碼事務就不會生效:
@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,這時候事務就會生效瞭。或者指定異常讓事務生效,比如 @Transactional(rollbackFor = Exception.class),這樣碰到所有的異常事務都會生效瞭。
為什麼加瞭@Transactional註解事物就生效瞭?
這是因為Spring容器會為加瞭這個註解的對象生成一個代理對象,實際調用的時候,實際上是調用的代理對象。 代理對象的實現瞭AOP的增強,實現瞭事務的實現。
通過註解怎麼實現指定的傳播特性和隔離級別的?
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 {}; }
代碼中可以看出,我們可以指定隔離級別和傳播特性,在Spring為我們生成代理類的時候,會讀取這些屬性,體現在增強邏輯中。
事務失效的8種情況及解決辦法
數據庫引擎不支持事務
這裡以 MySQL 為例,其 MyISAM 引擎是不支持事務操作的,InnoDB 才是支持事務的引擎,一般要支持事務都會使用 InnoDB,這時候選擇支持事務的數據庫即可(好像是廢話,哈哈哈)
沒有被 Spring 管理
這個好像沒什麼可說的,脫離瞭Spring的管理,還談什麼Spring事務管理。
方法不是 public 的
@Transactional 隻能用於 public 的方法上,否則事務不會失效,如果要用在非 public 方法上,可以開啟 AspectJ 代理模式。
數據源沒有配置事務管理器
相當於沒開啟事務管理,如果不是Springboot情況需要進行如下操作。
@Bean public PlatformTransactionManager transactionManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource); }
如果是SpringBoot,在啟動類上直接加上註解@EnableTransactionManagement即可。
傳播特性配錯瞭
傳播特性配置成,Propagation.SUPPORTED或者Propagation.NOT_SUPPORTED,改成支持事務的傳播特性即可。
異常類型錯誤
因為默認的異常類型是運行時異常,如果拋出瞭其他異常就不生效。
解決方式:
1、將異常改成運行時異常
2、指定異常進行事務回滾,如:@Transactional(rollbackFor = Exception.class)
異常被吃掉瞭
如果你代碼這麼寫,事務不生效:
@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事務管理,不會走到回滾邏輯
類內部調用
@Service public class UserInfoService { public void justUpdate(){ updateUser2(); } @Transactional(rollbackFor = Exception.class) public void updateUser2() { } }
上述代碼不生效,因為內部調用不會涉及到代理類的調用,更不會有AOP的增強,因此不會生效。
解決辦法:
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、獲取他的代理類,直接調用代理類
@Service public class UserInfoService { public void justUpdate(){ ((UserInfoService) AopContext.currentProxy()).updateUser2(); } @Transactional(rollbackFor = Exception.class) public void updateUser2() { } }
總結
到此這篇關於Spring事務是如何管理的的文章就介紹到這瞭,更多相關Spring事務管理內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Spring中@Transactional(rollbackFor=Exception.class)屬性用法介紹
- Spring超詳細講解事務
- Java Spring AOP源碼解析之事務實現原理
- Java spring事務及事務不生效的原因詳解
- springBoot service層事務控制的操作