所有的數據訪問技術都有事務處理機制,這些技術提供了API用來開啟事務、提交事務來完成數據操作,或者在發生錯誤的時候回滾數據。而Spring的事務機制是用統一的機制來處理不同數據訪問技術的事務處理。Spring的事務機制提供了一個PlatformTransactionManager接口,不同的數據訪問技術的事務使用不同的接口實現。SpringBoot提供了非常方便的事務操作,通過注解就可以實現事務的回滾,非常方便快捷,下面我們就說一下如何進行事務操作。
編程式事務
編程式事務管理使用TransactionTemplate或者直接使用底層的PlatformTransactionManager。對於編程式事務管理,spring推薦使用TransactionTemplate。
數據訪問技術 實現
JDBC DataSourceTransactionManager
JPA JpaTransactionManager
Hibernate HibernateTransactionManager
JDO JdoTransactionManager
分布式事務 JtaTransactionManager
然后呢在代碼中可以這樣使用
@Resource
private DataSourceTransactionManager transactionManager;
public void testdelivery(){
//定義事務隔離級別,傳播行為,
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
//事務狀態類,通過PlatformTransactionManager的getTransaction方法根據事務定義獲取;
//獲取事務狀態后,Spring根據傳播行為來決定如何開啟事務
TransactionStatus status = transactionManager.getTransaction(def);
int i = userService.queryForInt();
System.out.println("表中記錄總數:"+i);
try {
userService.update("1");
transactionManager.commit(status); //提交status中綁定的事務
} catch (RuntimeException e) {
transactionManager.rollback(status); //回滾
}
i = userService.queryForInt(COUNT_SQL);
System.out.println("表中記錄總數:"+i);
}
聲明式事務
可知編程式事務每次實現都要單獨實現,但業務量大功能復雜時,使用編程式事務無疑是痛苦的,而聲明式事務不同,聲明式事務屬於無侵入式,不會影響業務邏輯的實現。
聲明式事務管理使用了 AOP 實現的,本質就是在目標方法執行前后進行攔截。在目標方法執行前加入或創建一個事務,在執行方法執行后,根據實際情況選擇提交或是回滾事務。
在這里插入圖片描述
@Transactional可以作用於接口、接口方法、類以及類方法上。當作用於類上時,該類的所有 public 方法將都具有該類型的事務屬性,同時,我們也可以在方法級別使用該標注來覆蓋類級別的定義。因此可以在Service層和Controller層使用。
在此處需要特別注意的是,此@Transactional注解來自org.springframework.transaction.annotation包,而不是javax.transaction。
這里特意說明一下
如果在接口、實現類或方法上都指定了@Transactional注解,則優先級順序為方法>實現類>接口;
建議只在實現類或實現類的方法上使用@Transactional,而不要在接口上使用,這是因為如果使用JDK代理機制(基於接口的代理)是沒問題;而使用使用CGLIB代理(繼承)機制時就會遇到問題,因為其使用基於類的代理而不是接口,這是因為接口上的@Transactional注解是“不能繼承的”;
Spring提供了一個@EnableTransactionManagement注解在配置類上來開啟聲明式事務的支持。使用了@EnableTransactionManagement后,Spring容器會自動掃描注解@Transactional的方法和類。
常用配置
參 數 名 稱 功 能 描 述
readOnly 該屬性用於設置當前事務是否為只讀事務,設置為true表示只讀,false則表示可讀寫,默認值為false。例如:@Transactional(readOnly=true)
rollbackFor 該屬性用於設置需要進行回滾的異常類數組,當方法中拋出指定異常數組中的異常時,則進行事務回滾。例如:指定單一異常類:@Transactional(rollbackFor=RuntimeException.class)指定多個異常類:@Transactional(rollbackFor={RuntimeException.class, Exception.class})
rollbackForClassName 該屬性用於設置需要進行回滾的異常類名稱數組,當方法中拋出指定異常名稱數組中的異常時,則進行事務回滾。例如:指定單一異常類名稱@Transactional(rollbackForClassName=”RuntimeException”)指定多個異常類名稱:@Transactional(rollbackForClassName={“RuntimeException”,”Exception”})
noRollbackFor 該屬性用於設置不需要進行回滾的異常類數組,當方法中拋出指定異常數組中的異常時,不進行事務回滾。例如:指定單一異常類:@Transactional(noRollbackFor=RuntimeException.class)指定多個異常類:@Transactional(noRollbackFor={RuntimeException.class, Exception.class})
noRollbackForClassName 該屬性用於設置不需要進行回滾的異常類名稱數組,當方法中拋出指定異常名稱數組中的異常時,不進行事務回滾。例如:指定單一異常類名稱:@Transactional(noRollbackForClassName=”RuntimeException”)指定多個異常類名稱:@Transactional(noRollbackForClassName={“RuntimeException”,”Exception”})
propagation 該屬性用於設置事務的傳播行為。例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
isolation 該屬性用於設置底層數據庫的事務隔離級別,事務隔離級別用於處理多事務並發的情況,通常使用數據庫的默認隔離級別即可,基本不需要進行設置
timeout 該屬性用於設置事務的超時秒數,默認值為-1表示永不超時
事務屬性
事務屬性包含以下五個方面:隔離級別、傳播行為、回滾規則、事務超時、只讀。
事務隔離級別
隔離級別是指若干個並發的事務之間的隔離程度。TransactionDefinition 接口中定義了五個表示隔離級別的常量:
TransactionDefinition.ISOLATION_DEFAULT:這是默認值,表示使用底層數據庫的默認隔離級別。對大部分數據庫而言,通常這值就是TransactionDefinition.ISOLATION_READ_COMMITTED
TransactionDefinition.ISOLATION_READ_UNCOMMITTED:該隔離級別表示一個事務可以讀取另一個事務修改但還沒有提交的數據。該級別不能防止臟讀,不可重復讀和幻讀,因此很少使用該隔離級別。比如PostgreSQL實際上並沒有此級別。
TransactionDefinition.ISOLATION_READ_COMMITTED:該隔離級別表示一個事務只能讀取另一個事務已經提交的數據。該級別可以防止臟讀,這也是大多數情況下的推薦值。
TransactionDefinition.ISOLATION_REPEATABLE_READ:該隔離級別表示一個事務在整個過程中可以多次重復執行某個查詢,並且每次返回的記錄都相同。該級別可以防止臟讀和不可重復讀。
TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事務依次逐個執行,這樣事務之間就完全不可能產生干擾,也就是說,該級別可以防止臟讀、不可重復讀以及幻讀。但是這將嚴重影響程序的性能。通常情況下也不會用到該級別。
事務傳播行為
所謂事務的傳播行為是指,如果在開始當前事務之前,一個事務上下文已經存在,此時有若干選項可以指定一個事務性方法的執行行為。在TransactionDefinition定義中包括了如下幾個表示傳播行為的常量:
TransactionDefinition.PROPAGATION_REQUIRED:如果當前存在事務,則加入該事務;如果當前沒有事務,則創建一個新的事務。這是默認值。
TransactionDefinition.PROPAGATION_REQUIRES_NEW:創建一個新的事務,如果當前存在事務,則把當前事務掛起。
TransactionDefinition.PROPAGATION_SUPPORTS:如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續運行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務方式運行,如果當前存在事務,則把當前事務掛起。
TransactionDefinition.PROPAGATION_NEVER:以非事務方式運行,如果當前存在事務,則拋出異常。
TransactionDefinition.PROPAGATION_MANDATORY:如果當前存在事務,則加入該事務;如果當前沒有事務,則拋出異常。
TransactionDefinition.PROPAGATION_NESTED:如果當前存在事務,則創建一個事務作為當前事務的嵌套事務來運行;如果當前沒有事務,則該取值等價於TransactionDefinition.PROPAGATION_REQUIRED。
事務超時
所謂事務超時,就是指一個事務所允許執行的最長時間,如果超過該時間限制但事務還沒有完成,則自動回滾事務。在 TransactionDefinition 中以 int 的值來表示超時時間,其單位是秒。
默認設置為底層事務系統的超時值,如果底層數據庫事務系統沒有設置超時值,那么就是none,沒有超時限制。
事務只讀屬性
只讀事務用於客戶代碼只讀但不修改數據的情形,只讀事務用於特定情景下的優化,比如使用Hibernate的時候。
默認為讀寫事務。
注意點
僅對 public 方法有效
只有 @Transactional 注解應用到 public 方法上才能進行事務管理。這是因為 Spring 在 AOP 事務注解時,在讀取注解上的屬性方法中,會優先判斷方法是否是 public,如果不是 public,就不會讀取事務配置信息。
AOP 的自調用問題
在 Spring 的 AOP 代理下,只有目標方法由外部調用,目標方法才由 Spring 生成的代理對象來管理。也就是說,在同一個類中的一個 @Transactional 方法中,去掉用另一個 @Transactional 方法,會導致第二個方法的事務無效,被 Spring AOP 所忽略。
@Service
public class TestServiceImpl implements TestService {
@Transactional
public void transaction1() {
transaction2(); // transaction2 事務無效
}
@Transactional
public void transaction2() { ... }
}
對方法try-catch
對方法進行try-catch后 捕捉異常,則事物就失效了,如果既想try-catch又想事物回歸怎么辦呢?這樣就行了。
@Service
public class TestServiceImpl implements TestService {
@Transactional(rollbackFor = Exception.class)
public boolean transaction1() {
try{
xxxService.save()
return true;
}catch(Exception e){
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();//進行手動回滾
return fasle;
}
}
}
補充一下:
//設置回滾點
Object savePoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint();
....
//回滾到savePoint
TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint);
————————————————