Spring框架中的事務
有很多人覺得我們有了Spring,就再也不需要去處理獲得連接、事務提交、回滾和關閉連接等這些操作了,其實並不是這樣的,事實上Spring並不是直接管理事務的,只是提供了多種事務管理器,讓持久化機制所提供的平臺框架的事務來實現事務管理。
Spring事務管理的三大接口底層的實現關係如圖所示:
三者的關係非常清晰,TransactionDefinition 將 Transaction 傳給 PlatformTransactionManager ,TransactionStatus 又從PlatformTransactionManager 得到Transaction 。
具體的PlatformTransactionManager實現類又有4種,它們是針對不同的數據庫或者框架而定製的。包括:
- DataSourceTransactionManager
- HibernateTransactionManager
- JpaTransactionManager
- JtaTranscationManager
Spring事務管理的API
Spring事務管理主要包括3個接口,Spring的事務主要是由他們三個共同完成的,分別是:PlatformTransactionManager,TransactionDefinition,TransactionStatus。其中的PlatformTransactionManager是Spring事務管理的核心接口!這三個事務管理器接口是根據指定的傳播行爲,返回當前活動的事務,或者創建一個新的事務,參數的類定義一些基本的事務屬性。
PlatformTransactionManager
第一個接口是PlatformTransactionManager,是Spring事務管理的核心接口。主要功能是事務管理器,是用於平臺相關事務的管理,包括commit 事務的提交;rollback 事務的回滾;getTransaction 事務狀態的獲取三種方法。
TransactionDefinition
第二個接口是TransactionDefinition,主要功能是事務定義信息,是用來定義事務相關的屬性,給事務管理器PlatformTransactionManager使用的。而且在TransactionDefinition接口中定義了它自己的傳播行爲和隔離級別。包括getIsolationLevel:獲取隔離級別;getPropagationBehavior:獲取傳播行爲;getTimeout:獲取超時時間;isReadOnly:是否只讀 四種方法。
TransactionStatus
第三個接口是TransactionStatus,主要功能是事務具體運行狀態,是事務管理過程中,每個時間點事務的狀態信息,它可以封裝許多代碼,節省我們的工作量。包括hasSavepoint():返回這個事務內部是否包含一個保存點;isCompleted():返回該事務是否已完成,也就是說,是否已經提交或回滾;isNewTransaction():判斷當前事務是否是一個新事務 這三種方法。
Spring如何配置事務管理器
看完以上的三個接口,那麼你知道Spring是如何配置事務管理器的嗎?
1. 編程式事務管理
通過PlatformTransactionManager實現來進行事務管理。這種方式在實際開發中,很少使用,基本都是使用聲明式事務管理。
<!--配置事務管理的模板-->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transacationManager />
<property name="isolationLevelName" value="ISOLATION_DEFAULT" />
<property name="propagationBeHaviorName" value="PROPAGETION_REQUIRED" />
</bean>
2. 聲明式事務管理
聲明式事務管理有又有兩種不同的方式:一種是基於tx和aop命名空間的xml配置文件,也稱配置Aspectj方式的聲明式事務,另一種是使用註解的聲明式事務
Aspectj方式的聲明式事務
步驟:
- maven依賴
1-1 spring-aop
1-2 spring-aspects
1-3 aspectjweaver - spring-root.xml配置文件中引入命名空間
- 配置Aspectj方法的聲明式事務、
3-1 配置事務管理器
3-2 配置事務通知
3-3 配置aop:config [切入點、通知]
1.maven依賴
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.2</version>
</dependency>
2. spring-root配置文件引入命名空間
需要使用的命名空間有tx和aop。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
">
3.配置Aspectj方式的聲明式事務
- 配置事務管理器
- 配置事務通知
- 配置aop:config (切入點 通知)
具體代碼:
<!-- 事務管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManag er">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置事務通知 -->
<tx:advice id="tx" transaction-manager="transactionManager">
<tx:attributes>
<!-- name:表示以什麼開頭的方法 -->
<!-- propagation:傳播行爲 -->
<!-- isolation:隔離級別 -->
<tx:method name="add*" propagation="REQUIRED" isolation="DEFAULT" />
<tx:method name="insert*" propagation="REQUIRED" isolation="DEFAULT" />
<tx:method name="update*" propagation="REQUIRED" isolation="DEFAULT" />
<tx:method name="delete*" propagation="REQUIRED" isolation="DEFAULT" />
<tx:method name="del*" propagation="REQUIRED" isolation="DEFAULT" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="tx-point" expression="execution(* com.golden3young.service..*.*(..))" />
<aop:advisor advice-ref="tx" pointcut-ref="tx-point" />
</aop:config>
關於代碼中的execution(* com.golden3young.service….(…)),解釋一下是什麼意思。這是一個表達式:開頭的 * 表示 任意方法權限修飾符, com.golden3young.service… 表示com.golden3young.service包及其所有子包下的內容,至於是什麼內容,由緊接着的 *. *(…)來指定,意思是任意類型下的任意方法。(…)表示任意的方法參數
重點就是 一個 * 表示當前層級的所有 , 兩個 * 表示當前層級和所有子層級
使用註解方式的聲明式事務
步驟:
- 在spring-root.xml配置文件中配置事務管理器 (不需要配置事務通知)
- 直接在代碼使用事務的地方寫Transaction註解即可
1.配置事務管理器
<!-- 事務管理器 -->
<bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManag er">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置註解掃描驅動,用於自動掃描寫在代碼中的註解 -->
<tx:annotation-driven transaction-manager="tx" />
根據配置的代碼可以看出來,依賴於命名空間tx,所以也需要提前引入。
2.在代碼中直接使用註解
@Transactional
@Override
public int addUser(User user) {
return userMapper.addUser(user);
}
例如,在Service層中調用dao層方法的時候,我們就可以直接使用@Transactional註解,Spring容器將自動進行掃描,爲我們構建對應的事務管理。
知道了事務具體的配置方法,那麼下面我們再來研究一下,在配置過程中,可以配置的參數都用哪些,以及對應着哪些功能。
spring事務的隔離級別
spring提供了五種隔離級:
- ISOLATION_DEFAULT:默認的 這是默認值,表示使用底層數據庫的默認隔離級別。對大部分數據庫而言,通常這值就是 ISOLATION_READ_COMMITTED。
- ISOLATION_READ_UNCOMMITTED:未提交讀 該隔離級別表示一個事務可以讀取另一個事務修改但還沒有提交的數據。 該級別不能防止髒讀、不可重複讀和幻讀,因此很少使用該隔離級別。
- ISOLATION_READ_COMMITTED:已提交讀 該隔離級別表示一個事務只能讀取另一個事務已經提交的數據。 該級別可以防止髒讀,這也是大多數情況下的推薦值。
- ISOLATION_REPEATABLE_READ:可重複讀 該隔離級別表示一個事務在整個過程中可以多次重複執行某個查詢,並且每次返回的記錄都相 同。 該級別可以防止髒讀、不可重複讀。
- ISOLATION_SERIALIZABLE:序列化 所有的事務依次逐個執行,這樣事務之間就完全不可能產生干擾; 也就是說,該級別可以防止髒讀、不可重複讀以及幻讀。 但是這將嚴重影響程序的性能。通常情況下也不會用到該級別。
spring事務的傳播行爲
當多個具有事務的方法直接存在依賴關係時,事務是如何傳播的呢?我們可以進行設置。
- REQUIRED:表示如果當前存在一個事務,則加入該事務,否則將新建一個事務;
解釋:
method A(){
methodB();
}
method B(){
}
如果A方法和B方法的事務傳播行爲都是 REQUIRED 等級,方法A中調用了方法B,那麼如果A和B都有事務,B將加入到A的事務中。如果A沒有事務,而B有事務,B將創建自己的事務。
- REQUIRES_NEW:表示不管是否存在事務,都創建一個新的事務,原來的掛起,新 的執行完畢,繼續執行老的事務;
method A(){
methodB();
}
method B(){}
如果A\B都有事務 --B 將暫停A的事務
如果A沒有事務,B有事務 ---B 將創建自己的事務
- SUPPORTS:表示如果當前存在事務,就加入該事務;如果當前沒有事務,那就不使 用事務;
method A(){
methodB();
}
method B(){}
如果A\B都有事務 --B 將加入到A 的事務中
如果A沒有事務,B有事務 ---B 將不使用任何事務
- NOT_SUPPORTED: 表示不使用事務;如果當前存在事務,就把當前事務暫停,以 非事務方式執行;
method A(){ //
methodB();
}
method B(){}
不管A有沒有事務,B都不用。A如果有,B就給它暫停,如果沒有那正好。
- MANDATORY:表示必須在一個已有的事務中執行,如果當前沒有事務,則拋出異常;
method A(){
methodB();
}
method B(){ //PROPAGATION_MANDATORY
}
如果方法B的事務傳播等級爲MANDATORY
那它必須以事務的方式運行
調用的地方必須要有事務,如果沒有事務,B將直接拋異常
- NEVER:表示以非事務方式執行,如果當前存在事務,則拋出異常;
method A(){ //
methodB();
}
method B(){ //
}
必須以沒有事務的方式運行
調用的地方必須沒有事務,如果有事務,B將直接拋異常
- NESTED:這個是嵌套事務; 如果當前存在事務,則在嵌套事務內執行; 如果當前不存在事務,則創建一個新的事務;
嵌套事務使用數據庫中的保存點來實現,即嵌套事務回滾不影響外部事務,但外部事務回滾將導致嵌套事務回滾;
學到了事務中具體的屬性和屬性值,那麼我們的事務配置就可以進行自定義的指定了。例如註解可以這樣書寫:@Transacational(propagation=Propagetion.REQUIRED,isolation = Isolation.DEFAULT)
也就是在註解的括號中,增加了propagation屬性和isolation屬性並賦值。
spring事務的回滾機制
Java的世界裏,有兩種異常:運行時異常和非運行時異常。根據名字即可輕鬆的辨別出它們的區別。
運行時異常是在代碼運行過程中出現的例外情況。運行時異常是不需要捕獲的,程序員可以不去處理,當異常出現時,虛擬機會處理,當然如果虛擬機處理不了,最終還是得程序員去處理。
非運行時異常(也叫checked異常)就必須得捕獲了,否則連編譯都不過去,java編譯器要求程序員必須對這種異常進行catch,Java認爲Checked異常都是可以被處理(修復)的異常,所以Java程序必須顯式處理Checked異常。
在處理非運行時異常時,有兩種處理辦法:一種是拋給上級,一種是try-catch自己消化掉。
在spring事務的回滾機制中,其作用就是當指定的方法拋出異常時,會觸發事務的回滾機制,來將剛纔對數據庫的修改進行回滾。
- spring事務默認只對運行時異常(非檢查型異常)起作用
- spring可以指定需要處理的異常類型,使用rollbackFor指定需要處理的異常
- spring也可以指定不需要處理的異常,使用noRollbackFor指定不需要處理的異常;
默認情況下,Spring只有在拋出的異常是運行時異常(“非檢查型”)時纔回滾該事務; 也就是拋出的異常爲RuntimeException的子類(Errors也會導致事務回滾); 而拋出非運行時異常(檢查型)則不會導致事務回滾; 但是,我們可以明確的配置拋出哪些異常時回滾事務,包括checked異常。也可以定義哪些異常拋出時不回滾事務。
我們再來將剛纔那個註解,添加上回滾機制中的屬性和屬性值:
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, rollbackFor = Exception.class, noRollbackFor = XxxExcetion.class )
總結
所謂Spring事務,也就是將數據庫中的事務放到了Spring中,其實Spring是整合了一下各種數據庫中的事務,將不同數據庫中的事務進行總結,這樣,一旦使用了Spring框架,就可以直接將事務管理交給Spring來處理,不再需要像之前面向數據庫來自己處理事務。既然數據庫的種類繁多,各數據庫中的事務種類又各不相同,所以在整合過程中,就存在着多種情況,這就是爲什麼在本文一開頭就介紹了Spring事務的底層接口和實現類,以及它們直接是如何進行工作的。隨後,由於事務處理中,有各種屬性,如:隔離級別、傳播行爲、回滾機制,所以,我們在使用spring管理事務的時候,可以通過屬性+屬性值來進行設置,剩下的工作就全部交給Spring去幫助我們完成。配置Spring事務的方法又有兩種,編程式和聲明式,一般在實際開發中,使用聲明式居多,同時,Aspectj方式在springmvc中使用較多,註解方式在springboot中使用較多。不過,這都不是固定的,在掌握了不同的配置方法後,如何配置完全由自己決定,當然,我們都偏愛簡潔迅速的方式。