事務管理一般有編程式和聲明式兩種,編程式是直接在代碼中進行編寫事物處理過程,而聲名式則是通過注解方式或者是在xml文件中進行配置,相對編程式很方便。
而注解方式通過@Transactional 是常見的。我們可以使用@EnableTransactionManagement 注解來啟用事務管理功能,該注解可以加在啟動類上或者單獨加個配置類來處理。
1、Transactional 注解的屬性
- name 當在配置文件中有多個 TransactionManager , 可以用該屬性指定選擇哪個事務管理器。
- propagation 事務的傳播行為,默認值為 REQUIRED。
- isolation 事務的隔離度,默認值采用 DEFAULT。
- timeout 事務的超時時間,默認值為-1。如果超過該時間限制但事務還沒有完成,則自動回滾事務。
- read-only 指定事務是否為只讀事務,默認值為 false;為了忽略那些不需要事務的方法,比如讀取數據,可以設置 read-only 為 true。
- rollback-for 用於指定能夠觸發事務回滾的異常類型,如果有多個異常類型需要指定,各類型之間可以通過逗號分隔。
- no-rollback- for 拋出 no-rollback-for 指定的異常類型,不回滾事務。
propagation 屬性(事務傳播性)
- REQUIRED 支持當前已經存在的事務,如果還沒有事務,就創建一個新事務。
- MANDATORY 支持當前已經存在的事務,如果還沒有事務,就拋出一個異常。
- NESTED 在當前事務中創建一個嵌套事務,如果還沒有事務,那么就簡單地創建一個新事務。
- REQUIRES_NEW 掛起當前事務,創建一個新事務,如果還沒有事務,就簡單地創建一個新事務。
- NEVER 強制要求不在事務中運行,如果當前存在一個事務,則拋出異常。
- NOT_SUPPORTED 強制不在事務中運行,如果當前存在一個事務,則掛起該事務。
- SUPPORTS 支持當前事務,如果沒有事務那么就不在事務中運行。
2、Transactional應用
@Transactional 可以加在方法上,表示對當前方法配置事務也可以添加到類級別上。
也可以添加到類級別上。當把@Transactional 注解放在類級別時,表示所有該類的公共方法都配置相同的事務屬性信息。
當類級別配置了@Transactional,方法級別也配置了@Transactional,應用程序會以方法級別的事務屬性信息來管理事務,即方法級別的事務屬性信息會覆蓋類級別的相關配置信息。
3、Transactional工作原理
聲明式事務管理包含三個組成部分:
-
事務的切面
-
事務管理器
- EntityManager Proxy本身
事務的切面
事務的切面是一個“around(環繞)”切面,在注解的業務方法前后都可以被調用。實現切面的具體類是TransactionInterceptor。事務的切面有兩個主要職責:
-
在’before’時,切面提供一個調用點,來決定被調用業務方法應該在正在進行事務的范圍內運行,還是開始一個新的獨立事務。
-
在’after’時,切面需要確定事務被提交,回滾或者繼續運行。
在’before’時,事務切面自身不包含任何決策邏輯,是否開始新事務的決策委派給事務管理器完成。
事務管理器
事務管理器需要解決下面兩個問題:
-
新的Entity Manager是否應該被創建?
-
是否應該開始新的事務?
這些需要事務切面’before’邏輯被調用時決定。事務管理器的決策基於以下兩點:
-
事務是否正在進行
-
事務方法的propagation屬性(比如REQUIRES_NEW總要開始新事務)
如果事務管理器確定要創建新事務,那么將:
-
創建一個新的entity manager
-
entity manager綁定到當前線程
-
從數據庫連接池中獲取連接
-
將連接綁定到當前線程
使用ThreadLocal變量將entity manager和數據庫連接都綁定到當前線程。事務運行時他們存儲在線程中,當它們不再被使用時,事務管理器決定是否將他們清除。程序的任何部分如果需要當前的entity manager和數據庫連接都可以從線程中獲取。
EntityManager proxy
當業務方法調用類似entityManager.persist()方法時,這不是由entity manager直接調用的,而是業務方法調用代理,因為事物管理器將entity manage綁定到了線程上,代理從線程獲取當前的entity manager。
4、附注
4.1 @Transactional 注解應用到 public 方法,才能進行事務管理。因為aop會進行攔截是否是public方法:
//AbstractFallbackTransactionAttributeSource類 protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) { // Don't allow no-public methods as required. if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { return null;
}
}
4.2 propagation 屬性
下面三種 propagation 可以不啟動事務。錯誤的配置這三種 propagation,事務可能不會發生回滾。
- TransactionDefinition.PROPAGATION_SUPPORTS:如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續運行。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務方式運行,如果當前存在事務,則把當前事務掛起。
- TransactionDefinition.PROPAGATION_NEVER:以非事務方式運行,如果當前存在事務,則拋出異常。
4.3 rollbackFor 屬性
默認情況下,如果在事務中拋出了未檢查異常(繼承自 RuntimeException 的異常)或者 Error,則 Spring 將回滾事務;除此之外,Spring 不會回滾事務。
可以通過rollbackFor來制定在事物中拋出的其他類型的異常來支持事務回滾,例:
@Transactional(propagation= Propagation.REQUIRED, rollbackFor= MyException.class)
若在目標方法中拋出的異常是 rollbackFor 指定的異常的子類,事務同樣會回滾。
4.4 在默認的代理模式下,只有目標方法由外部調用,才能被 Spring 的事務攔截器攔截。在同一個類中的兩個方法直接調用,是不會被 Spring 的事務攔截器攔截
像如下這種在同一個類中的兩個方法上加上事務控制,其中method上的事務是不能生效的,一種方法就是把它寫到另一個列中,然后再當前類中調用
@Transactional(propagation = Propagation.REQUIRED) @Override public void save() { method(); 。。。業務處理 if (true) { throw new RuntimeException("save 拋異常了"); } } @Transactional(propagation = Propagation.REQUIRES_NEW) public void method() { 。。。業務處理 }
今日話:
生活或許就是這樣吧,忙的時候你想着閑下來,閑下來太久了又想着忙些什么。