Spring提供了一致的事務(wù)管理抽象。這個(gè)抽象是Spring最重要的抽象之一, 提供如下的優(yōu)點(diǎn):
傳統(tǒng)上,J2EE開發(fā)者有兩個(gè)事務(wù)管理的選擇: 全局或 局部事務(wù)。全局事務(wù)由應(yīng)用服務(wù)器管理,使用JTA。局部 事務(wù)是資源相關(guān)的:例如,一個(gè)和JDBC連接關(guān)聯(lián)的事務(wù)。這個(gè)選擇有深刻的含義。 全局事務(wù)提供和多個(gè)參與事務(wù)的資源(需要指出的是多數(shù)應(yīng)用使用單一參與事務(wù) 的資源)。使用局部事務(wù),應(yīng)用服務(wù)器不需要參與事務(wù)管理,并且不能幫助確保 跨越多個(gè)資源的正確性。
全局事務(wù)有一個(gè)顯著的不利方面,代碼需要使用JTA:一個(gè)笨重的API(部分是 因?yàn)樗漠惓DP停?。此外,JTA UserTransaction通常需 要從JNDI獲得,意味著我需要同時(shí)使用JNDI和JTA來(lái)使用 JTA。顯然全部使用全局事務(wù)限制了應(yīng)用代碼的重用性,因?yàn)镴TA通常只在應(yīng)用服 務(wù)器的環(huán)境中才能使用。
首選的使用全局事務(wù)的方式是通過(guò)EJB的CMT (容器管理的事務(wù)): 一種形式的 聲明式事務(wù)管理(區(qū)別于編程式事務(wù)管理 )。EJB的CMT消除了事務(wù)相關(guān)的JNDI查找的需求,雖然使用EJB本身 肯定需要使用JNDI。它消除大多數(shù)――不是全部――書寫Java代碼控制事務(wù)的需求。 顯著的不利方面是CMT和JTA以及應(yīng)用服務(wù)器環(huán)境捆綁在一起,并且只有我們選擇 使用EJB實(shí)現(xiàn)業(yè)務(wù)邏輯時(shí)才能使用,或者至少使用在一個(gè)事務(wù)化EJB的外觀 (Fa?ade)后。EJB有如此多的詬病,當(dāng)存在可供選擇的聲明式事務(wù)管理時(shí), EJB不是一個(gè)吸引人的建議。
局部事務(wù)容易使用,但也有明顯的不利方面:它們不能跨越多個(gè)參與事務(wù)的資 源使用,并且趨向侵入的編程模型。例如,使用JDBC連接事務(wù)管理的代碼不能使 在全局的JTA事務(wù)中。
Spring解決了這些問題。它使應(yīng)用開發(fā)者能夠使用在任何環(huán)境 下使用一致的編程模型。你可以只寫一次你的代碼,這些代碼可以從在不同環(huán)境 下的不同事務(wù)管理策略中獲益。Spring同時(shí)提供聲明式和編程式事務(wù)管理。
使用編程式事務(wù)管理,開發(fā)者使用Spring事務(wù)抽象,這個(gè)抽象可以使用的任何 底層事務(wù)基礎(chǔ)之上。使用首選的聲明式模型,開發(fā)者通常書寫很少的事務(wù)相關(guān)代 碼,因此不依賴Spring或任何其他事務(wù)API。
Spring提供兩種方式的編程式事務(wù)管理
我們通常推薦使用第一種方式。
第二種方式類似使用JTA UserTransaction API (雖然異常處理少一點(diǎn)麻煩)。
Spring也提供了聲明式事務(wù)管理。這是通過(guò)Spring AOP實(shí)現(xiàn)的。
從考慮EJB CMT和Spring聲明式事務(wù)管理的相似以及不同之處出發(fā)是很有益的。 基本方法是一致的:都可以指定事務(wù)管理到單獨(dú)的方法;如果需要可以在事務(wù)上 下文調(diào)用setRollbackOnly()方法。不同之處:
-
不同于EJB CMT綁定在JTA上,Spring聲明式事務(wù)管理可以在任何環(huán)境下使用。 只需更改配置文件,它就可以和JDBC、JDO、Hibernate或其他的事務(wù)機(jī)制一起工作
-
Spring可以使聲明式事務(wù)管理應(yīng)用到POJO,不僅僅是特定的類,如EJB
-
Spring提供聲明式回滾規(guī)則:EJB沒有對(duì)應(yīng)的特性, 我們將在下面討論這個(gè)特性?;貪L可以聲明式控制,不僅僅是編程式的
-
Spring通過(guò)AOP提供定制事務(wù)行為的機(jī)會(huì)。例如,如果需要,你可以在事務(wù) 回滾中插入定制的行為。你也可以增加事務(wù)通知一樣增加任意的通知。使用 EJB CMT,除了使用setRollbackOnly()沒有其他的辦法能 夠影響容器的事務(wù)管理
-
Spring不提供高端應(yīng)用服務(wù)器提供的跨越遠(yuǎn)程調(diào)用的事務(wù)上下文傳播。如 果你需要這些特性,我們推薦你使用EJB。然而,不要過(guò)度使用這些特性。通常我 們并不希望事務(wù)跨越遠(yuǎn)程調(diào)用
回滾規(guī)則的概念是很重要的:它們使得我們可以指定那些異常應(yīng)該發(fā)起一個(gè)自 動(dòng)回滾。我們?cè)谂渲梦募卸皇荍ava代碼中指定這些聲明。因此,雖然我們?nèi)?然可以編程式調(diào)用TransactionStatus對(duì)象的 setRollbackOnly()方法來(lái)回滾當(dāng)前事務(wù),多數(shù)時(shí)候我們可以 指定規(guī)則如MyApplicationException應(yīng)該用于導(dǎo)致一個(gè)回滾。 這有顯著的優(yōu)點(diǎn),業(yè)務(wù)對(duì)象不需要依賴事務(wù)基礎(chǔ)設(shè)施。例如,他們通常不需要引 入任何Spring API,事務(wù)或其他任何東西。
EJB的默認(rèn)行為是遇到系統(tǒng)異常(通常是運(yùn)行時(shí)異常) EJB容器自動(dòng)回滾事務(wù)。EJB CMT遇到應(yīng)用程序異常 (除了java.rmi.RemoteException外被檢查的異常)時(shí)不 會(huì)自動(dòng)回滾事務(wù)。雖然Spring聲明式事務(wù)管理沿用EJB CMT約定(遇到不被檢查的 異常自動(dòng)回滾事務(wù)),但是這是可以定制的。
按照我們的基準(zhǔn),Spring聲明式事務(wù)管理的性能要?jiǎng)龠^(guò)EJB CMT。
通常設(shè)置Spring事務(wù)代理的方法是通過(guò)TransactionProxyFactoryBean。我們需 要一個(gè)包裝在事務(wù)代理中目標(biāo)對(duì)象。這個(gè)目標(biāo)對(duì)象一般是一個(gè)POJO的bean。當(dāng)我 們定義TransactionProxyFactoryBean時(shí),必須提供一個(gè)相關(guān)的 PlatformTransactionManager的引用和事務(wù)屬性。 事務(wù)屬性含有上面描述的事務(wù)定義。
<bean id="petStore"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref bean="transactionManager"/></property>
<property name="target"><ref bean="petStoreTarget"/></property>
<property name="transactionAttributes">
<props>
<prop key="insert*">PROPAGATION_REQUIRED,-MyCheckedException</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
事務(wù)代理會(huì)實(shí)現(xiàn)目標(biāo)對(duì)象的接口:這里是id為petStoreTarget.的bean(使用 CGLIB可以實(shí)現(xiàn)具體類的代理,只要設(shè)置proxyTargetClass屬性為true就可以。這 將自動(dòng)設(shè)置,如果目標(biāo)對(duì)象沒有實(shí)現(xiàn)任何接口。通常,我們希望面向接口而不是 類編程)。可以(一般來(lái)說(shuō)是好的想法)限定事務(wù)代理使用proxyInterfaces來(lái)代 理指定的接口。也可以通過(guò)繼承至 org.springframework.aop.framework.ProxyConfig幾個(gè)屬 性來(lái)定制TransactionProxyFactoryBean的行為,并在所有的AOP代理工廠中共享。
這里的transactionAttributes通過(guò)定在 org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource 類中的屬性格式來(lái)設(shè)置。這個(gè)包括通配符的方法名稱映射是很直觀的。注意 insert*的映射的值包括回滾規(guī)則。添加-MyCheckedException 指定如果方法拋出MyCheckedException或它的子類,事務(wù)將 會(huì)自動(dòng)回滾??梢灾付▊€(gè)回滾規(guī)則,用逗號(hào)分隔。-前綴強(qiáng)制回滾,+前綴指定提 交(這允許即使拋出未被檢查的異常時(shí)也可以提交事務(wù),當(dāng)然你自己要明白自己 在做什么)。
TransactionProxyFactoryBean允許你通過(guò) “preInterceptors”和“postInterceptors”屬性設(shè)置“前”或“后”通知來(lái)提供額外的 攔截行為。可以設(shè)置任意數(shù)量的“前”和“后”通知,它們的類型可以是 Advisor(可以包含一個(gè)pointcut), MethodInterceptor或被當(dāng)前Spring配置支持的通知類型 (例如ThrowAdvice, AfterReturningtAdvice或BeforeAdvice, 這些都是默認(rèn)支持的)。這些通知必須支持實(shí)例共享模式。如果你需要高級(jí)AOP特 性來(lái)使用事務(wù),如有狀態(tài)的混入,那最好使用通用的 org.springframework.aop.framework.ProxyFactoryBean, 而不是TransactionProxyFactoryBean代理創(chuàng)建者。
也可以設(shè)置自動(dòng)代理:配置AOP框架,不需要單獨(dú)的代理定義類就可以生成類的 代理。
更多信息和實(shí)例請(qǐng)參閱AOP章節(jié)。
如果你只有很少的事務(wù)操作,使用編程式事務(wù)管理是很好的主意。例如, 如果你有一個(gè)WEB應(yīng)用需要為某些更新操作提供事務(wù),你可能不想用Spring或其 他技術(shù)設(shè)置一個(gè)事務(wù)代理。使用 TransactionTemplate可能是個(gè)很好的方法。
另一方面,如果你的應(yīng)用有大量的事務(wù)操作,編程式(譯注:應(yīng)為聲明式, 疑是作者筆誤)事務(wù)管理通常是很有價(jià)值的。它使得事務(wù)管理從業(yè)務(wù)邏輯分離, 并且Spring中配置也不困難。使用Spring, 而不是EJB CMT,聲明式事務(wù)管理配置的成本極大地降低。
7.6. 你需要應(yīng)用服務(wù)器管理事務(wù)嗎?
Spring的事務(wù)管理能力――尤其聲明式事務(wù)管理極大地改變了J2EE應(yīng)用程序需要 應(yīng)用服務(wù)器的傳統(tǒng)想法。
特別地,你不需要應(yīng)用服務(wù)器僅僅為了通過(guò)EJB聲明事務(wù)。事實(shí)上,即使你擁 有強(qiáng)大JTA支持的應(yīng)用服務(wù)器,你也可以決定使用Spring聲明式事務(wù)管理提供比 EJB CMT更強(qiáng)大更高效的編程模型。
只有需要支持多個(gè)事務(wù)資源時(shí)你才需要應(yīng)用服務(wù)器的JTA支持。許多應(yīng)用沒有 這個(gè)需求。例如許多高端應(yīng)用使用單一的高度可升級(jí)的數(shù)據(jù)庫(kù),如Oracle 9i RAC。
當(dāng)然也許你需要其他應(yīng)用服務(wù)器的功能,如JMS和JCA。但是如果你只需使用 JTA,你可以考慮開源的JTA實(shí)現(xiàn),如JOTM(Spring整合了JOTM)。但是, 2004年早期,高端的應(yīng)用服務(wù)器提供更健壯的XA資源支持。
最重要一點(diǎn),使用Spring,你可以選擇何時(shí)將你的應(yīng)用遷移到完整 應(yīng)用服務(wù)器。除了使用EJB CMT和JTA只能書寫代碼使用局部事務(wù), 例如JDBC連接的事務(wù),而且如果曾經(jīng)需要全局的容器管理的事務(wù),還面臨著繁重 的改寫過(guò)程,這些日子一去不復(fù)返了。使用Spring只有配置需要改變,你的代碼 不需要。
開發(fā)著需要按照需求仔細(xì)的使用正確的 PlatformTransactionManager實(shí)現(xiàn)。
理解Spring事務(wù)抽象時(shí)如何和JTA全局事務(wù)一起工作是非常重要的。使用得當(dāng), 沒有任何沖突:Spring僅僅提供一個(gè)簡(jiǎn)單的,可以移植的抽象層。
如果你使用全局事務(wù),你必須為你的所有事務(wù)操作使用Spring的 org.springframework.transaction.jta.JtaTransactionManager。 否則Spring將試圖在資源如容器數(shù)據(jù)源執(zhí)行局部事務(wù)。這樣的局部事務(wù)沒有任何 意義,好的應(yīng)用服務(wù)器會(huì)把這作為一個(gè)錯(cuò)誤。