Spring 事務(wù)屬性分析
事務(wù)管理對(duì)于企業(yè)應(yīng)用而言至關(guān)重要肩碟。它保證了用戶的每一次操作都是可靠的强窖,即便出現(xiàn)了異常的訪問情況,也不至于破壞后臺(tái)數(shù)據(jù)的完整性削祈。就像銀行的自助取款機(jī)翅溺,通常都能正常為客戶服務(wù),但是也難免遇到操作過程中機(jī)器突然出故障的情況髓抑,此時(shí)咙崎,事務(wù)就必須確保出故障前對(duì)賬戶的操作不生效,就像用戶剛才完全沒有使用過取款機(jī)一樣吨拍,以保證用戶和銀行的利益都不受損失褪猛。
在 Spring 中,事務(wù)是通過 TransactionDefinition 接口來定義的羹饰。該接口包含與事務(wù)屬性有關(guān)的方法伊滋。具體如清單1所示:
清單1. TransactionDefinition 接口中定義的主要方法
public interface TransactionDefinition{
int getIsolationLevel();
int getPropagationBehavior();
int getTimeout();
boolean isReadOnly();
}
也許你會(huì)奇怪碳却,為什么接口只提供了獲取屬性的方法,而沒有提供相關(guān)設(shè)置屬性的方法笑旺。其實(shí)道理很簡單昼浦,事務(wù)屬性的設(shè)置完全是程序員控制的,因此程序員可以自定義任何設(shè)置屬性的方法筒主,而且保存屬性的字段也沒有任何要求关噪。唯一的要求的是,Spring 進(jìn)行事務(wù)操作的時(shí)候乌妙,通過調(diào)用以上接口提供的方法必須能夠返回事務(wù)相關(guān)的屬性取值使兔。
事務(wù)隔離級(jí)別
隔離級(jí)別是指若干個(gè)并發(fā)的事務(wù)之間的隔離程度。TransactionDefinition 接口中定義了五個(gè)表示隔離級(jí)別的常量:
TransactionDefinition.ISOLATION_DEFAULT:這是默認(rèn)值冠胯,表示使用底層數(shù)據(jù)庫的默認(rèn)隔離級(jí)別火诸。對(duì)大部分?jǐn)?shù)據(jù)庫而言锦针,通常這值就是TransactionDefinition.ISOLATION_READ_COMMITTED荠察。
TransactionDefinition.ISOLATION_READ_UNCOMMITTED:該隔離級(jí)別表示一個(gè)事務(wù)可以讀取另一個(gè)事務(wù)修改但還沒有提交的數(shù)據(jù)。該級(jí)別不能防止臟讀和不可重復(fù)讀奈搜,因此很少使用該隔離級(jí)別悉盆。
TransactionDefinition.ISOLATION_READ_COMMITTED:該隔離級(jí)別表示一個(gè)事務(wù)只能讀取另一個(gè)事務(wù)已經(jīng)提交的數(shù)據(jù)。該級(jí)別可以防止臟讀馋吗,這也是大多數(shù)情況下的推薦值焕盟。
TransactionDefinition.ISOLATION_REPEATABLE_READ:該隔離級(jí)別表示一個(gè)事務(wù)在整個(gè)過程中可以多次重復(fù)執(zhí)行某個(gè)查詢,并且每次返回的記錄都相同宏粤。即使在多次查詢之間有新增的數(shù)據(jù)滿足該查詢脚翘,這些新增的記錄也會(huì)被忽略。該級(jí)別可以防止臟讀和不可重復(fù)讀绍哎。
TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事務(wù)依次逐個(gè)執(zhí)行来农,這樣事務(wù)之間就完全不可能產(chǎn)生干擾,也就是說崇堰,該級(jí)別可以防止臟讀沃于、不可重復(fù)讀以及幻讀。但是這將嚴(yán)重影響程序的性能海诲。通常情況下也不會(huì)用到該級(jí)別繁莹。
事務(wù)傳播行為
所謂事務(wù)的傳播行為是指,如果在開始當(dāng)前事務(wù)之前特幔,一個(gè)事務(wù)上下文已經(jīng)存在咨演,此時(shí)有若干選項(xiàng)可以指定一個(gè)事務(wù)性方法的執(zhí)行行為。在TransactionDefinition定義中包括了如下幾個(gè)表示傳播行為的常量:
TransactionDefinition.PROPAGATION_REQUIRED:如果當(dāng)前存在事務(wù)蚯斯,則加入該事務(wù)薄风;如果當(dāng)前沒有事務(wù)零院,則創(chuàng)建一個(gè)新的事務(wù)。
TransactionDefinition.PROPAGATION_REQUIRES_NEW:創(chuàng)建一個(gè)新的事務(wù)村刨,如果當(dāng)前存在事務(wù)告抄,則把當(dāng)前事務(wù)掛起。
TransactionDefinition.PROPAGATION_SUPPORTS:如果當(dāng)前存在事務(wù)嵌牺,則加入該事務(wù)打洼;如果當(dāng)前沒有事務(wù),則以非事務(wù)的方式繼續(xù)運(yùn)行逆粹。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務(wù)方式運(yùn)行募疮,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起僻弹。
TransactionDefinition.PROPAGATION_NEVER:以非事務(wù)方式運(yùn)行阿浓,如果當(dāng)前存在事務(wù),則拋出異常蹋绽。
TransactionDefinition.PROPAGATION_MANDATORY:如果當(dāng)前存在事務(wù)芭毙,則加入該事務(wù);如果當(dāng)前沒有事務(wù)卸耘,則拋出異常退敦。
TransactionDefinition.PROPAGATION_NESTED:如果當(dāng)前存在事務(wù),則創(chuàng)建一個(gè)事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來運(yùn)行蚣抗;如果當(dāng)前沒有事務(wù)侈百,則該取值等價(jià)于TransactionDefinition.PROPAGATION_REQUIRED。
這里需要指出的是翰铡,前面的六種事務(wù)傳播行為是 Spring 從 EJB 中引入的钝域,他們共享相同的概念。而PROPAGATION_NESTED是 Spring 所特有的锭魔。以 PROPAGATION_NESTED 啟動(dòng)的事務(wù)內(nèi)嵌于外部事務(wù)中(如果存在外部事務(wù)的話)例证,此時(shí),內(nèi)嵌事務(wù)并不是一個(gè)獨(dú)立的事務(wù)赂毯,它依賴于外部事務(wù)的存在战虏,只有通過外部的事務(wù)提交,才能引起內(nèi)部事務(wù)的提交党涕,嵌套的子事務(wù)不能單獨(dú)提交烦感。如果熟悉 JDBC 中的保存點(diǎn)(SavePoint)的概念,那嵌套事務(wù)就很容易理解了膛堤,其實(shí)嵌套的子事務(wù)就是保存點(diǎn)的一個(gè)應(yīng)用手趣,一個(gè)事務(wù)中可以包括多個(gè)保存點(diǎn),每一個(gè)嵌套子事務(wù)。另外绿渣,外部事務(wù)的回滾也會(huì)導(dǎo)致嵌套子事務(wù)的回滾朝群。
事務(wù)超時(shí)
所謂事務(wù)超時(shí),就是指一個(gè)事務(wù)所允許執(zhí)行的最長時(shí)間中符,如果超過該時(shí)間限制但事務(wù)還沒有完成姜胖,則自動(dòng)回滾事務(wù)。在 TransactionDefinition 中以 int 的值來表示超時(shí)時(shí)間淀散,其單位是秒右莱。
事務(wù)的只讀屬性
事務(wù)的只讀屬性是指,對(duì)事務(wù)性資源進(jìn)行只讀操作或者是讀寫操作档插。所謂事務(wù)性資源就是指那些被事務(wù)管理的資源慢蜓,比如數(shù)據(jù)源、 JMS 資源郭膛,以及自定義的事務(wù)性資源等等晨抡。如果確定只對(duì)事務(wù)性資源進(jìn)行只讀操作,那么我們可以將事務(wù)標(biāo)志為只讀的则剃,以提高事務(wù)處理的性能耘柱。在 TransactionDefinition 中以 boolean 類型來表示該事務(wù)是否只讀。
事務(wù)的回滾規(guī)則
通常情況下忍级,如果在事務(wù)中拋出了未檢查異常(繼承自 RuntimeException 的異常)帆谍,則默認(rèn)將回滾事務(wù)。如果沒有拋出任何異常轴咱,或者拋出了已檢查異常,則仍然提交事務(wù)烈涮。這通常也是大多數(shù)開發(fā)者希望的處理方式朴肺,也是 EJB 中的默認(rèn)處理方式。但是坚洽,我們可以根據(jù)需要人為控制事務(wù)在拋出某些未檢查異常時(shí)任然提交事務(wù)戈稿,或者在拋出某些已檢查異常時(shí)回滾事務(wù)。
Spring 事務(wù)管理 API 分析
Spring 框架中讶舰,涉及到事務(wù)管理的 API 大約有100個(gè)左右鞍盗,其中最重要的有三個(gè):TransactionDefinition、PlatformTransactionManager跳昼、TransactionStatus般甲。所謂事務(wù)管理,其實(shí)就是“按照給定的事務(wù)規(guī)則來執(zhí)行提交或者回滾操作”鹅颊》蟠妫“給定的事務(wù)規(guī)則”就是用 TransactionDefinition 表示的,“按照……來執(zhí)行提交或者回滾操作”便是用 PlatformTransactionManager 來表示堪伍,而 TransactionStatus 用于表示一個(gè)運(yùn)行著的事務(wù)的狀態(tài)锚烦。打一個(gè)不恰當(dāng)?shù)谋扔髅倜觯琓ransactionDefinition 與 TransactionStatus 的關(guān)系就像程序和進(jìn)程的關(guān)系。
TransactionDef...
該接口在前面已經(jīng)介紹過涮俄,它用于定義一個(gè)事務(wù)蛉拙。它包含了事務(wù)的靜態(tài)屬性,比如:事務(wù)傳播行為彻亲、超時(shí)時(shí)間等等刘离。Spring 為我們提供了一個(gè)默認(rèn)的實(shí)現(xiàn)類:DefaultTransactionDefinition,該類適用于大多數(shù)情況睹栖。如果該類不能滿足需求硫惕,可以通過實(shí)現(xiàn) TransactionDefinition 接口來實(shí)現(xiàn)自己的事務(wù)定義。
PlatformTrans...
PlatformTransactionManager 用于執(zhí)行具體的事務(wù)操作野来。接口定義如清單2所示:
清單2. PlatformTransactionManager 接口中定義的主要方法
Public interface PlatformTransactionManager{
TransactionStatus getTransaction(TransactionDefinition definition)
throws TransactionException;
void commit(TransactionStatus status)throws TransactionException;
void rollback(TransactionStatus status)throws TransactionException;
}
根據(jù)底層所使用的不同的持久化 API 或框架恼除,PlatformTransactionManager 的主要實(shí)現(xiàn)類大致如下:
DataSourceTransactionManager:適用于使用JDBC和iBatis進(jìn)行數(shù)據(jù)持久化操作的情況。
HibernateTransactionManager:適用于使用Hibernate進(jìn)行數(shù)據(jù)持久化操作的情況曼氛。
JpaTransactionManager:適用于使用JPA進(jìn)行數(shù)據(jù)持久化操作的情況豁辉。
另外還有JtaTransactionManager 、JdoTransactionManager舀患、JmsTransactionManager等等徽级。
如果我們使用JTA進(jìn)行事務(wù)管理,我們可以通過 JNDI 和 Spring 的 JtaTransactionManager 來獲取一個(gè)容器管理的 DataSource聊浅。JtaTransactionManager 不需要知道 DataSource 和其他特定的資源餐抢,因?yàn)樗鼘⑹褂萌萜魈峁┑娜质聞?wù)管理。而對(duì)于其他事務(wù)管理器低匙,比如DataSourceTransactionManager旷痕,在定義時(shí)需要提供底層的數(shù)據(jù)源作為其屬性,也就是 DataSource顽冶。與 HibernateTransactionManager 對(duì)應(yīng)的是 SessionFactory欺抗,與 JpaTransactionManager 對(duì)應(yīng)的是 EntityManagerFactory 等等。
TransactionStatus
PlatformTransactionManager.getTransaction(…) 方法返回一個(gè) TransactionStatus 對(duì)象强重。返回的TransactionStatus 對(duì)象可能代表一個(gè)新的或已經(jīng)存在的事務(wù)(如果在當(dāng)前調(diào)用堆棧有一個(gè)符合條件的事務(wù))绞呈。TransactionStatus 接口提供了一個(gè)簡單的控制事務(wù)執(zhí)行和查詢事務(wù)狀態(tài)的方法。該接口定義如清單3所示:
清單3. TransactionStatus 接口中定義的主要方法
public interface TransactionStatus{
boolean isNewTransaction();
void setRollbackOnly();
boolean isRollbackOnly();
}
編程式事務(wù)管理
Spring 的編程式事務(wù)管理概述
在 Spring 出現(xiàn)以前间景,編程式事務(wù)管理對(duì)基于 POJO 的應(yīng)用來說是唯一選擇佃声。用過 Hibernate 的人都知道,我們需要在代碼中顯式調(diào)用beginTransaction()拱燃、commit()秉溉、rollback()等事務(wù)管理相關(guān)的方法,這就是編程式事務(wù)管理。通過 Spring 提供的事務(wù)管理 API召嘶,我們可以在代碼中靈活控制事務(wù)的執(zhí)行父晶。在底層,Spring 仍然將事務(wù)操作委托給底層的持久化框架來執(zhí)行弄跌。
基于底層 API 的編程式事務(wù)管理
根據(jù)PlatformTransactionManager甲喝、TransactionDefinition 和 TransactionStatus 三個(gè)核心接口,我們完全可以通過編程的方式來進(jìn)行事務(wù)管理铛只。示例代碼如清單4所示:
清單4. 基于底層 API 的事務(wù)管理示例代碼
public class BankServiceImpl implements BankService {
private BankDao bankDao;
private TransactionDefinition txDefinition;
private PlatformTransactionManager txManager;
......
public boolean transfer(Long fromId埠胖, Long toId, double amount) {
TransactionStatus txStatus = txManager.getTransaction(txDefinition);
boolean result = false;
try {
result = bankDao.transfer(fromId淳玩, toId直撤, amount);
txManager.commit(txStatus);
} catch (Exception e) {
result = false;
txManager.rollback(txStatus);
System.out.println("Transfer Error!");
}
return result;
}
}
相應(yīng)的配置文件如清單5所示:
清單5. 基于底層API的事務(wù)管理示例配置文件
<bean id="bankService" class="footmark.spring.core.tx.programmatic.origin.BankServiceImpl">
<property name="bankDao" ref="bankDao"/>
<property name="txManager" ref="transactionManager"/>
<property name="txDefinition">
<bean class="org.springframework.transaction.support.DefaultTransactionDefinition">
<property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/>
</bean>
</property>
</bean>
如上所示,我們?cè)陬愔性黾恿藘蓚€(gè)屬性:一個(gè)是 TransactionDefinition 類型的屬性蜕着,它用于定義一個(gè)事務(wù)谋竖;另一個(gè)是 PlatformTransactionManager 類型的屬性,用于執(zhí)行事務(wù)管理操作承匣。
如果方法需要實(shí)施事務(wù)管理蓖乘,我們首先需要在方法開始執(zhí)行前啟動(dòng)一個(gè)事務(wù),調(diào)用PlatformTransactionManager.getTransaction(...) 方法便可啟動(dòng)一個(gè)事務(wù)韧骗。創(chuàng)建并啟動(dòng)了事務(wù)之后嘉抒,便可以開始編寫業(yè)務(wù)邏輯代碼,然后在適當(dāng)?shù)牡胤綀?zhí)行事務(wù)的提交或者回滾袍暴。
基于 TransactionTemplate 的編程式事務(wù)管理
通過前面的示例可以發(fā)現(xiàn)些侍,這種事務(wù)管理方式很容易理解,但令人頭疼的是容诬,事務(wù)管理的代碼散落在業(yè)務(wù)邏輯代碼中娩梨,破壞了原有代碼的條理性,并且每一個(gè)業(yè)務(wù)方法都包含了類似的啟動(dòng)事務(wù)览徒、提交/回滾事務(wù)的樣板代碼。幸好颂龙,Spring 也意識(shí)到了這些习蓬,并提供了簡化的方法,這就是 Spring 在數(shù)據(jù)訪問層非常常見的模板回調(diào)模式措嵌。如清單6所示:
清單6. 基于 TransactionTemplate 的事務(wù)管理示例代碼
public class BankServiceImpl implements BankService {
private BankDao bankDao;
private TransactionTemplate transactionTemplate;
......
public boolean transfer(final Long fromId官研, final Long toId复局, final double amount) {
return (Boolean) transactionTemplate.execute(new TransactionCallback(){
public Object doInTransaction(TransactionStatus status) {
Object result;
try {
result = bankDao.transfer(fromId, toId, amount);
} catch (Exception e) {
status.setRollbackOnly();
result = false;
System.out.println("Transfer Error!");
}
return result;
}
});
}
}
相應(yīng)的XML配置如下:
清單 7. 基于 TransactionTemplate 的事務(wù)管理示例配置文件
<bean id="bankService" class="footmark.spring.core.tx.programmatic.template.BankServiceImpl">
<property name="bankDao" ref="bankDao"/>
<property name="transactionTemplate" ref="transactionTemplate"/>
</bean>
TransactionTemplate 的 execute() 方法有一個(gè) TransactionCallback 類型的參數(shù)序调,該接口中定義了一個(gè) doInTransaction() 方法睛约,通常我們以匿名內(nèi)部類的方式實(shí)現(xiàn) TransactionCallback 接口,并在其 doInTransaction() 方法中書寫業(yè)務(wù)邏輯代碼。這里可以使用默認(rèn)的事務(wù)提交和回滾規(guī)則探孝,這樣在業(yè)務(wù)代碼中就不需要顯式調(diào)用任何事務(wù)管理的 API。doInTransaction() 方法有一個(gè)TransactionStatus 類型的參數(shù)誉裆,我們可以在方法的任何位置調(diào)用該參數(shù)的 setRollbackOnly() 方法將事務(wù)標(biāo)識(shí)為回滾的顿颅,以執(zhí)行事務(wù)回滾。
根據(jù)默認(rèn)規(guī)則足丢,如果在執(zhí)行回調(diào)方法的過程中拋出了未檢查異常粱腻,或者顯式調(diào)用了TransacationStatus.setRollbackOnly() 方法,則回滾事務(wù)斩跌;如果事務(wù)執(zhí)行完成或者拋出了 checked 類型的異常绍些,則提交事務(wù)。
TransactionCallback 接口有一個(gè)子接口 TransactionCallbackWithoutResult耀鸦,該接口中定義了一個(gè) doInTransactionWithoutResult() 方法柬批,TransactionCallbackWithoutResult 接口主要用于事務(wù)過程中不需要返回值的情況。當(dāng)然揭糕,對(duì)于不需要返回值的情況萝快,我們?nèi)匀豢梢允褂?TransactionCallback 接口,并在方法中返回任意值即可著角。
聲明式事務(wù)管理
Spring 的聲明式事務(wù)管理概述
Spring 的聲明式事務(wù)管理在底層是建立在 AOP 的基礎(chǔ)之上的揪漩。其本質(zhì)是對(duì)方法前后進(jìn)行攔截,然后在目標(biāo)方法開始之前創(chuàng)建或者加入一個(gè)事務(wù)吏口,在執(zhí)行完目標(biāo)方法之后根據(jù)執(zhí)行情況提交或者回滾事務(wù)奄容。
聲明式事務(wù)最大的優(yōu)點(diǎn)就是不需要通過編程的方式管理事務(wù),這樣就不需要在業(yè)務(wù)邏輯代碼中摻雜事務(wù)管理的代碼产徊,只需在配置文件中做相關(guān)的事務(wù)規(guī)則聲明(或通過等價(jià)的基于標(biāo)注的方式)昂勒,便可以將事務(wù)規(guī)則應(yīng)用到業(yè)務(wù)邏輯中。因?yàn)槭聞?wù)管理本身就是一個(gè)典型的橫切邏輯舟铜,正是 AOP 的用武之地戈盈。Spring 開發(fā)團(tuán)隊(duì)也意識(shí)到了這一點(diǎn),為聲明式事務(wù)提供了簡單而強(qiáng)大的支持谆刨。
聲明式事務(wù)管理曾經(jīng)是 EJB 引以為傲的一個(gè)亮點(diǎn)塘娶,如今 Spring 讓 POJO 在事務(wù)管理方面也擁有了和 EJB 一樣的待遇,讓開發(fā)人員在 EJB 容器之外也用上了強(qiáng)大的聲明式事務(wù)管理功能痊夭,這主要得益于 Spring 依賴注入容器和 Spring AOP 的支持刁岸。依賴注入容器為聲明式事務(wù)管理提供了基礎(chǔ)設(shè)施,使得 Bean 對(duì)于 Spring 框架而言是可管理的她我;而 Spring AOP 則是聲明式事務(wù)管理的直接實(shí)現(xiàn)者虹曙,這一點(diǎn)通過清單8可以看出來迫横。
通常情況下,筆者強(qiáng)烈建議在開發(fā)中使用聲明式事務(wù)酝碳,不僅因?yàn)槠浜唵畏猓饕且驗(yàn)檫@樣使得純業(yè)務(wù)代碼不被污染,極大方便后期的代碼維護(hù)击敌。
和編程式事務(wù)相比介返,聲明式事務(wù)唯一不足地方是,后者的最細(xì)粒度只能作用到方法級(jí)別沃斤,無法做到像編程式事務(wù)那樣可以作用到代碼塊級(jí)別圣蝎。但是即便有這樣的需求,也存在很多變通的方法衡瓶,比如徘公,可以將需要進(jìn)行事務(wù)管理的代碼塊獨(dú)立為方法等等。
下面就來看看 Spring 為我們提供的聲明式事務(wù)管理功能哮针。
基于 TransactionInter... 的聲明式事務(wù)管理
最初关面,Spring 提供了 TransactionInterceptor 類來實(shí)施聲明式事務(wù)管理功能。先看清單8的配置文件:
清單 8. 基于 TransactionInterceptor 的事務(wù)管理示例配置文件
<beans...>
......
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributes">
<props>
<prop key="transfer">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<bean id="bankServiceTarget" class="footmark.spring.core.tx.declare.origin.BankServiceImpl">
<property name="bankDao" ref="bankDao"/>
</bean>
<bean id="bankService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="bankServiceTarget"/>
<property name="interceptorNames">
<list><idref bean="transactionInterceptor"/></list>
</property>
</bean>
......
</beans>
首先十厢,我們配置了一個(gè) TransactionInterceptor 來定義相關(guān)的事務(wù)規(guī)則等太,他有兩個(gè)主要的屬性:一個(gè)是 transactionManager,用來指定一個(gè)事務(wù)管理器蛮放,并將具體事務(wù)相關(guān)的操作委托給它缩抡;另一個(gè)是 Properties 類型的 transactionAttributes 屬性,它主要用來定義事務(wù)規(guī)則包颁,該屬性的每一個(gè)鍵值對(duì)中瞻想,鍵指定的是方法名,方法名可以使用通配符娩嚼,而值就表示相應(yīng)方法的所應(yīng)用的事務(wù)屬性蘑险。
指定事務(wù)屬性的取值有較復(fù)雜的規(guī)則,這在 Spring 中算得上是一件讓人頭疼的事岳悟。具體的書寫規(guī)則如下:
傳播行為 [佃迄,隔離級(jí)別] [,只讀屬性] [贵少,超時(shí)屬性] [不影響提交的異常] [和屎,導(dǎo)致回滾的異常]
傳播行為是唯一必須設(shè)置的屬性,其他都可以忽略春瞬,Spring為我們提供了合理的默認(rèn)值。
傳播行為的取值必須以“PROPAGATION_”開頭套啤,具體包括:PROPAGATION_MANDATORY宽气、PROPAGATION_NESTED随常、PROPAGATION_NEVER、PROPAGATION_NOT_SUPPORTED萄涯、PROPAGATION_REQUIRED绪氛、PROPAGATION_REQUIRES_NEW、PROPAGATION_SUPPORTS涝影,共七種取值枣察。
隔離級(jí)別的取值必須以“ISOLATION_”開頭,具體包括:ISOLATION_DEFAULT燃逻、ISOLATION_READ_COMMITTED序目、ISOLATION_READ_UNCOMMITTED、ISOLATION_REPEATABLE_READ伯襟、ISOLATION_SERIALIZABLE猿涨,共五種取值。
如果事務(wù)是只讀的姆怪,那么我們可以指定只讀屬性叛赚,使用“readOnly”指定。否則我們不需要設(shè)置該屬性稽揭。
超時(shí)屬性的取值必須以“TIMEOUT_”開頭俺附,后面跟一個(gè)int類型的值,表示超時(shí)時(shí)間溪掀,單位是秒事镣。
不影響提交的異常是指,即使事務(wù)中拋出了這些類型的異常膨桥,事務(wù)任然正常提交蛮浑。必須在每一個(gè)異常的名字前面加上“+”。異常的名字可以是類名的一部分只嚣。比如“+RuntimeException”沮稚、“+tion”等等。
導(dǎo)致回滾的異常是指册舞,當(dāng)事務(wù)中拋出這些類型的異常時(shí)蕴掏,事務(wù)將回滾。必須在每一個(gè)異常的名字前面加上“-”调鲸。異常的名字可以是類名的全部或者部分盛杰,比如“-RuntimeException”、“-tion”等等藐石。
以下是兩個(gè)示例:
<property name="*Service">
PROPAGATION_REQUIRED即供,ISOLATION_READ_COMMITTED,TIMEOUT_20于微,
+AbcException逗嫡,+DefException青自,-HijException
</property>
以上表達(dá)式表示,針對(duì)所有方法名以 Service 結(jié)尾的方法驱证,使用 PROPAGATION_REQUIRED 事務(wù)傳播行為延窜,事務(wù)的隔離級(jí)別是 ISOLATION_READ_COMMITTED,超時(shí)時(shí)間為20秒抹锄,當(dāng)事務(wù)拋出 AbcException 或者 DefException 類型的異常逆瑞,則仍然提交,當(dāng)拋出 HijException 類型的異常時(shí)必須回滾事務(wù)伙单。這里沒有指定"readOnly"获高,表示事務(wù)不是只讀的。
<property name="test">PROPAGATION_REQUIRED车份,readOnly</property>
以上表達(dá)式表示谋减,針對(duì)所有方法名為 test 的方法,使用 PROPAGATION_REQUIRED 事務(wù)傳播行為扫沼,并且該事務(wù)是只讀的出爹。除此之外,其他的屬性均使用默認(rèn)值缎除。比如严就,隔離級(jí)別和超時(shí)時(shí)間使用底層事務(wù)性資源的默認(rèn)值,并且當(dāng)發(fā)生未檢查異常器罐,則回滾事務(wù)梢为,發(fā)生已檢查異常則仍提交事務(wù)。
配置好了 TransactionInterceptor轰坊,我們還需要配置一個(gè) ProxyFactoryBean 來組裝 target 和advice铸董。這也是典型的 Spring AOP 的做法。通過 ProxyFactoryBean 生成的代理類就是織入了事務(wù)管理邏輯后的目標(biāo)類肴沫。至此粟害,聲明式事務(wù)管理就算是實(shí)現(xiàn)了。我們沒有對(duì)業(yè)務(wù)代碼進(jìn)行任何操作颤芬,所有設(shè)置均在配置文件中完成悲幅,這就是聲明式事務(wù)的最大優(yōu)點(diǎn)。
基于 TransactionProxy... 的聲明式事務(wù)管理
前面的聲明式事務(wù)雖然好站蝠,但是卻存在一個(gè)非常惱人的問題:配置文件太多汰具。我們必須針對(duì)每一個(gè)目標(biāo)對(duì)象配置一個(gè) ProxyFactoryBean;另外菱魔,雖然可以通過父子 Bean 的方式來復(fù)用 TransactionInterceptor 的配置留荔,但是實(shí)際的復(fù)用幾率也不高;這樣澜倦,加上目標(biāo)對(duì)象本身存谎,每一個(gè)業(yè)務(wù)類可能需要對(duì)應(yīng)三個(gè) <bean/> 配置拔疚,隨著業(yè)務(wù)類的增多,配置文件將會(huì)變得越來越龐大既荚,管理配置文件又成了問題。
為了緩解這個(gè)問題栋艳,Spring 為我們提供了 TransactionProxyFactoryBean恰聘,用于將TransactionInterceptor 和 ProxyFactoryBean 的配置合二為一。如清單9所示:
清單9. 基于 TransactionProxyFactoryBean 的事務(wù)管理示例配置文件
<beans......>
......
<bean id="bankServiceTarget" class="footmark.spring.core.tx.declare.classic.BankServiceImpl">
<property name="bankDao" ref="bankDao"/>
</bean>
<bean id="bankService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="target" ref="bankServiceTarget"/>
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributes">
<props>
<prop key="transfer">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
......
</beans>
如此一來吸占,配置文件與先前相比簡化了很多晴叨。我們把這種配置方式稱為 Spring 經(jīng)典的聲明式事務(wù)管理。相信在早期使用 Spring 的開發(fā)人員對(duì)這種配置聲明式事務(wù)的方式一定非常熟悉矾屯。
但是兼蕊,顯式為每一個(gè)業(yè)務(wù)類配置一個(gè) TransactionProxyFactoryBean 的做法將使得代碼顯得過于刻板,為此我們可以使用自動(dòng)創(chuàng)建代理的方式來將其簡化件蚕,使用自動(dòng)創(chuàng)建代理是純 AOP 知識(shí)孙技,請(qǐng)讀者參考相關(guān)文檔,不在此贅述排作。
基于 <tx> 命名空間的聲明式事務(wù)管理
前面兩種聲明式事務(wù)配置方式奠定了 Spring 聲明式事務(wù)管理的基石牵啦。在此基礎(chǔ)上,Spring 2.x 引入了 <tx> 命名空間妄痪,結(jié)合使用 <aop> 命名空間哈雏,帶給開發(fā)人員配置聲明式事務(wù)的全新體驗(yàn),配置變得更加簡單和靈活衫生。另外裳瘪,得益于 <aop> 命名空間的切點(diǎn)表達(dá)式支持,聲明式事務(wù)也變得更加強(qiáng)大罪针。
如清單10所示:
清單10. 基于 <tx> 的事務(wù)管理示例配置文件
<beans......>
......
<bean id="bankService" class="footmark.spring.core.tx.declare.namespace.BankServiceImpl">
<property name="bankDao" ref="bankDao"/>
</bean>
<tx:advice id="bankAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transfer" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="bankPointcut" expression="execution(* *.transfer(..))"/>
<aop:advisor advice-ref="bankAdvice" pointcut-ref="bankPointcut"/>
</aop:config>
......
</beans>
如果默認(rèn)的事務(wù)屬性就能滿足要求彭羹,那么代碼簡化為如清單 11 所示:
清單 11. 簡化后的基于 <tx> 的事務(wù)管理示例配置文件
<beans......>
......
<bean id="bankService" class="footmark.spring.core.tx.declare.namespace.BankServiceImpl">
<property name="bankDao" ref="bankDao"/>
</bean>
<tx:advice id="bankAdvice" transaction-manager="transactionManager">
<aop:config>
<aop:pointcut id="bankPointcut" expression="execution(**.transfer(..))"/>
<aop:advisor advice-ref="bankAdvice" pointcut-ref="bankPointcut"/>
</aop:config>
......
</beans>
由于使用了切點(diǎn)表達(dá)式,我們就不需要針對(duì)每一個(gè)業(yè)務(wù)類創(chuàng)建一個(gè)代理對(duì)象了站故。另外皆怕,如果配置的事務(wù)管理器 Bean 的名字取值為“transactionManager”,則我們可以省略 <tx:advice> 的 transaction-manager 屬性西篓,因?yàn)樵搶傩缘哪J(rèn)值即為“transactionManager”愈腾。
基于 @Transactional 的聲明式事務(wù)管理
除了基于命名空間的事務(wù)配置方式,Spring 2.x 還引入了基于 Annotation 的方式岂津,具體主要涉及@Transactional 標(biāo)注虱黄。@Transactional 可以作用于接口、接口方法吮成、類以及類方法上橱乱。當(dāng)作用于類上時(shí)辜梳,該類的所有 public 方法將都具有該類型的事務(wù)屬性,同時(shí)泳叠,我們也可以在方法級(jí)別使用該標(biāo)注來覆蓋類級(jí)別的定義作瞄。如清單12所示:
清單12. 基于 @Transactional 的事務(wù)管理示例配置文件
@Transactional(propagation = Propagation.REQUIRED)
public boolean transfer(Long fromId, Long toId危纫, double amount) {
return bankDao.transfer(fromId宗挥, toId, amount);
}
Spring 使用 BeanPostProcessor 來處理 Bean 中的標(biāo)注种蝶,因此我們需要在配置文件中作如下聲明來激活該后處理 Bean契耿,如清單13所示:
清單13. 啟用后處理Bean的配置
<tx:annotation-driven transaction-manager="transactionManager"/>
與前面相似,transaction-manager 屬性的默認(rèn)值是 transactionManager螃征,如果事務(wù)管理器 Bean 的名字即為該值搪桂,則可以省略該屬性。
雖然 @Transactional 注解可以作用于接口盯滚、接口方法踢械、類以及類方法上,但是 Spring 小組建議不要在接口或者接口方法上使用該注解淌山,因?yàn)檫@只有在使用基于接口的代理時(shí)它才會(huì)生效裸燎。另外, @Transactional 注解應(yīng)該只被應(yīng)用到 public 方法上泼疑,這是由 Spring AOP 的本質(zhì)決定的德绿。如果你在 protected、private 或者默認(rèn)可見性的方法上使用 @Transactional 注解退渗,這將被忽略移稳,也不會(huì)拋出任何異常。
基于 <tx> 命名空間和基于 @Transactional 的事務(wù)聲明方式各有優(yōu)缺點(diǎn)会油「隽唬基于 <tx> 的方式,其優(yōu)點(diǎn)是與切點(diǎn)表達(dá)式結(jié)合翻翩,功能強(qiáng)大都许。利用切點(diǎn)表達(dá)式,一個(gè)配置可以匹配多個(gè)方法嫂冻,而基于 @Transactional 的方式必須在每一個(gè)需要使用事務(wù)的方法或者類上用 @Transactional 標(biāo)注胶征,盡管可能大多數(shù)事務(wù)的規(guī)則是一致的,但是對(duì) @Transactional 而言桨仿,也無法重用睛低,必須逐個(gè)指定。另一方面,基于 @Transactional 的方式使用起來非常簡單明了钱雷,沒有學(xué)習(xí)成本骂铁。開發(fā)人員可以根據(jù)需要,任選其中一種使用罩抗,甚至也可以根據(jù)需要混合使用這兩種方式拉庵。
如果不是對(duì)遺留代碼進(jìn)行維護(hù),則不建議再使用基于 TransactionInterceptor 以及基于TransactionProxyFactoryBean 的聲明式事務(wù)管理方式澄暮,但是名段,學(xué)習(xí)這兩種方式非常有利于對(duì)底層實(shí)現(xiàn)的理解。
雖然上面共列舉了四種聲明式事務(wù)管理方式泣懊,但是這樣的劃分只是為了便于理解,其實(shí)后臺(tái)的實(shí)現(xiàn)方式是一樣的麻惶,只是用戶使用的方式不同而已馍刮。
結(jié)束語
本教程的知識(shí)點(diǎn)大致總結(jié)如下:
基于 TransactionDefinition、PlatformTransactionManager窃蹋、TransactionStatus 編程式事務(wù)管理是 Spring 提供的最原始的方式卡啰,通常我們不會(huì)這么寫,但是了解這種方式對(duì)理解 Spring 事務(wù)管理的本質(zhì)有很大作用警没。
基于 TransactionTemplate 的編程式事務(wù)管理是對(duì)上一種方式的封裝匈辱,使得編碼更簡單、清晰杀迹。
基于 TransactionInterceptor 的聲明式事務(wù)是 Spring 聲明式事務(wù)的基礎(chǔ)亡脸,通常也不建議使用這種方式,但是與前面一樣树酪,了解這種方式對(duì)理解 Spring 聲明式事務(wù)有很大作用浅碾。
基于 TransactionProxyFactoryBean 的聲明式事務(wù)是上中方式的改進(jìn)版本,簡化的配置文件的書寫续语,這是 Spring 早期推薦的聲明式事務(wù)管理方式垂谢,但是在 Spring 2.0 中已經(jīng)不推薦了。
基于 <tx> 和 <aop> 命名空間的聲明式事務(wù)管理是目前推薦的方式疮茄,其最大特點(diǎn)是與 Spring AOP 結(jié)合緊密滥朱,可以充分利用切點(diǎn)表達(dá)式的強(qiáng)大支持,使得管理事務(wù)更加靈活力试。
基于 @Transactional 的方式將聲明式事務(wù)管理簡化到了極致徙邻。開發(fā)人員只需在配置文件中加上一行啟用相關(guān)后處理 Bean 的配置,然后在需要實(shí)施事務(wù)管理的方法或者類上使用 @Transactional 指定事務(wù)規(guī)則即可實(shí)現(xiàn)事務(wù)管理懂版,而且功能也不必其他方式遜色鹃栽。