Spring - Java/J2EE Application Framework 应用框架 第 7 章 事务管理
第?7?章?事務管理
7.1.?Spring事務抽象
Spring提供了一致的事務管理抽象。這個抽象是Spring最重要的抽象之一, 它有如下的優點:
-
為不同的事務API提供一致的編程模型,如JTA、JDBC、Hibernate、iBATIS數據庫層 和JDO
-
提供比大多數事務API更簡單的,易于使用的編程式事務管理API
-
整合Spring數據訪問抽象
-
支持Spring聲明式事務管理
傳統上,J2EE開發者有兩個事務管理的選擇:?全局事務或?局部事務。全局事務由應用服務器管理,使用JTA。局部 事務是和資源相關的:例如,一個和JDBC連接關聯的事務。這個選擇有深刻的含義。 全局事務可以用于多個事務性的資源(需要指出的是多數應用使用單一事務性 的資源)。使用局部事務,應用服務器不需要參與事務管理,并且不能幫助確保 跨越多個資源的事務的正確性。
全局事務有一個顯著的不利方面,代碼需要使用JTA:一個笨重的API(部分是 因為它的異常模型)。此外,JTA的UserTransaction通常需 要從JNDI獲得,這意味著我為了JTA需要同時使用JNDI和JTA。 顯然全部使用全局事務限制了應用代碼的重用性,因為JTA通常只在應用服 務器的環境中才能使用。
使用全局事務的比較好的方法是通過EJB的CMT?(容器管理的事務):?聲明式事務管理的一種形式(區別于編程式事務管理?)。EJB的CMT不需要任何和事務相關的JNDI查找,雖然使用EJB本身 肯定需要使用JNDI。它消除大多數——不是全部——書寫Java代碼控制事務的需求。 顯著的缺點是CMT綁定在JTA和應用服務器環境上,并且只有我們選擇 使用EJB實現業務邏輯,或者至少處于一個事務化EJB的外觀(Facade)后 才能使用它。EJB有如此多的詬病,當存在其它聲明式事務管理時, EJB不是一個吸引人的建議。
局部事務容易使用,但也有明顯的缺點:它們不能用于多個事務性資 源,并且趨向侵入的編程模型。例如,使用JDBC連接事務管理的代碼不能用于 全局的JTA事務中。
Spring解決了這些問題。它使應用開發者能夠使用在任何環境?下使用一致的編程模型。你可以只寫一次你的代碼,這在不同環境 下的不同事務管理策略中很有益處。Spring同時提供聲明式和編程式事務管理。
使用編程式事務管理,開發者直接使用Spring事務抽象,這個抽象可以使用在任何 底層事務基礎之上。使用首選的聲明式模型,開發者通常書寫很少的事務相關代 碼,因此不依賴Spring或任何其他事務API。
7.2.?事務策略
Spring事務抽象的關鍵是事務策略的概念。
這個概念由?org.springframework.transaction.PlatformTransactionManager?接口體現,如下:
public interface PlatformTransactionManager {TransactionStatus getTransaction(TransactionDefinition definition)throws TransactionException;void commit(TransactionStatus status) throws TransactionException;void rollback(TransactionStatus status) throws TransactionException; }這首先是一個SPI接口,雖然它也可以在編碼中使用。注意按照Spring的哲學, 這是一個接口。因而如果需要它可以很容易地被模擬和 樁化。它也沒有和一個查找策略如JNDI捆綁在一起:PlatformTransactionManager 的實現定義和其他Spring IoC容器中的對象一樣。這個好處使得即使使用JTA,也 是一個很有價值的抽象:事務代碼可以比直接使用JTA更加容易測試。
繼續Spring哲學,TransactionException是unchecked的。 低層的事務失敗幾乎都是致命。很少情況下應用程序代碼可以從它們 中恢復,不過應用開發者依然可以捕獲并處理?TransactionException。
getTransaction()根據一個類型為?TransactionDefinition的參數返回一個?TransactionStatus對象。返回的?TransactionStatus對象可能代表一個新的或已經存在的事 務(如果在當前調用堆棧有一個符合條件的事務)。
如同J2EE事務上下文一樣,一個TransactionStatus也是和執 行的線程關聯的。
TransactionDefinition接口指定:
-
事務隔離:當前事務和其它事務的隔離的程度。 例如,這個事務能否看到其他事務未提交的寫數據?
-
事務傳播:通常在一個事務中執行的 所有代碼都會在這個事務中運行。但是,如果一個事務上下文已經存在, 有幾個選項可以指定一個事務性方法的執行行為:例如,簡單地在現有的 事務中運行(大多數情況);或者掛起現有事務,創建一個新的事務。 Spring提供EJB CMT中熟悉的事務傳播選項。
-
事務超時: 事務在超時前能運行多 久(自動被底層的事務基礎設施回滾)。
-
只讀狀態: 只讀事務不修改任何數 據。只讀事務在某些情況下(例如當使用Hibernate時)可可是一種非常有用的優化。
這些設置反映了標準概念。如果需要,請查閱討論事務隔離層次和其他核心事 務概念的資源:理解這些概念在使用Spring和其他事務管理解決方案時是非常關鍵的。
TransactionStatus接口為處理事務的代碼提供一個簡單 的控制事務執行和查詢事務狀態的方法。這個概念應該是熟悉的,因為它們在所 有的事務API中是相同的:
public interface TransactionStatus {boolean isNewTransaction();void setRollbackOnly();boolean isRollbackOnly(); }但是使用Spring事務管理時,定義?PlatformTransactionManager的實現是基本方式。在好的Spring 風格中,這個重要定義使用IoC實現。
PlatformTransactionManager實現通常需要了解它們工作 的環境:JDBC、JTA、Hibernate等等。
下面來自Spring范例jPetstore中的?dataAccessContext-local.xml,它展示了一個局部 PlatformTransactionManager實現是如何定義的。它將和JDBC一起工作。
我們必須定義JDBC數據源,然后使用DataSourceTransactionManager,提供給 它的一個數據源引用。
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"><property name="driverClassName"><value>${jdbc.driverClassName}</value></property><property name="url"><value>${jdbc.url}</value></property><property name="username"><value>${jdbc.username}</value></property><property name="password"><value>${jdbc.password}</value></property> </bean>PlatformTransactionManager定義如下:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource"><ref local="dataSource"/></property> </bean>如果我們使用JTA,如同范例中dataAccessContext-jta.xml, 我們需要使用通過JNDI獲得的容器數據源,和一個JtaTransactionManager實 現。JtaTransactionManager不需要知道數據源,或任何其他特定資源,因為它將 使用容器的全局事務管理。
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"><property name="jndiName"><value>jdbc/jpetstore</value></property> </bean><bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>我們可以很容易地使用Hibernate局部事務,如同下面Spring的PetClinic?示例應用中的例子的一樣。
在這種情況下,我們需要定義一個Hibernate的LocalSessionFactory,應用程 序將使用它獲得Hibernate的會話。
數據源bean定義和上面例子類似,這里不再羅列(如果這是容器數據源,它應該是非事務的,因為Spring會管理事務, 而不是容器)。
這種情況下,“transactionManager” bean的類型是HibernateTransactionManager。 和DataSourceTransactionManager擁有一個數據源的引用一樣, HibernateTransactionManager需要一個SessionFactory的引用。
<bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean"><property name="dataSource"><ref local="dataSource"/></property><property name="mappingResources"><value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value></property><property name="hibernateProperties"><props><prop key="hibernate.dialect">${hibernate.dialect}</prop></props></property> </bean><bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager"><property name="sessionFactory"><ref local="sessionFactory"/></property> </bean>使用Hibernate和JTA事務,我們可以簡單地使用JtaTransactionManager, 就象JDBC或任何其他資源策略一樣。
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>注意任何資源的JTA配置都是這樣的,因為它們都是全局事務。
在所有這些情況下,應用程序代碼不需要任何更改。我們可以僅僅更改配置 來更改管理事務的方式,即使這些更改意味這從局部事務轉換到全局事務或者相反 的轉換。如果不使用全局事務,你需要采用一個特定的編碼規范。幸運的是它非常簡單 。你需要以一個特殊的方式獲得連接資源或者會話資源,允許相關的 PlatformTransactionManager實現跟蹤連接的使用,并且當需要時應用事務管理。
例如,如果使用JDBC,你不應該調用一個數據源的?getConnection()方法,而必須使用Spring的?org.springframework.jdbc.datasource.DataSourceUtils類,如下:
Connection conn = DataSourceUtils.getConnection(dataSource);這將提供額外的好處,任何SQLException都被Spring的?CannotGetJdbcConnectionException封裝起來,它屬于Spring的unchecked 的DataAccessException的類層次。這給你比 簡單地從SQLException獲得更多的信息,并且確保跨數據 庫,甚至跨越不同持久化技術的可移植性。
沒有Spring事務管理的情況下,這也能很好地工作,因此無論使用 Spring事務管理與否,你都可以使用它。
當然,一旦你使用Spring的JDBC支持或Hibernate支持,你將不想使用?DataSourceUtils或其他幫助類,因為與直接使用相關API相比,你將更樂意使用Spring的抽象。 例如,如果你使用Spring的JdbcTemplate或jdbc.object包來簡化使用JDBC, 正確的數據庫連接將自動取得,你不需要書寫任何特殊代碼。
7.3.?編程式事務管理
Spring提供兩種方式的編程式事務管理
-
使用TransactionTemplate
-
直接使用一個PlatformTransactionManager實現
我們通常推薦使用第一種方式。
第二種方式類似使用JTA?UserTransaction?API (雖然異常處理少一點麻煩)。
7.3.1.?使用TransactionTemplate
TransactionTemplate采用和其他Spring模板?,如JdbcTemplate和?HibernateTemplate,一樣的方法。它使用回調方法,把應用 程序代碼從處理取得和釋放資源中解脫出來(不再有try/catch/finally)。如同 其他模板,TransactionTemplate是線程安全的。
必須在事務上下文中執行的應用代碼看起來像這樣,注意使用?TransactionCallback可以返回一個值:
Object result = tt.execute(new TransactionCallback() {public Object doInTransaction(TransactionStatus status) {updateOperation1();return resultOfUpdateOperation2();} });如果沒有返回值,使用TransactionCallbackWithoutResult, 如下:
tt.execute(new TransactionCallbackWithoutResult() {protected void doInTransactionWithoutResult(TransactionStatus status) {updateOperation1();updateOperation2();} });回調中的代碼可以調用TransactionStatus對象的?setRollbackOnly()方法來回滾事務。
想要使用TransactionTemplate的應用類必須能訪問一 個PlatformTransactionManager:通常通過一個JavaBean屬 性或構造函數參數暴露出來。
使用模擬或樁化的PlatformTransactionManager,單元測試 這些類很簡單。沒有JNDI查找和靜態魔術代碼:它只是一個簡單的接口。和平常一樣, 你可以使用Spring簡化單元測試。
7.3.2.?使用PlatformTransactionManager
你也可以使用?org.springframework.transaction.PlatformTransactionManager?直接管理事務。簡單地通過一個bean引用給你的bean傳遞一個你使用的?PlatformTransactionManager實現。然后, 使用TransactionDefinition和?TransactionStatus對象就可以發起事務,回滾和提交。
DefaultTransactionDefinition def = new DefaultTransactionDefinition() def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);TransactionStatus status = transactionManager.getTransactionDefinition(def);try {// execute your business logic here } catch (MyException ex) {transactionManager.rollback(status);throw ex; } transactionManager.commit(status);7.4.?聲明式事務管理
Spring也提供了聲明式事務管理。這是通過Spring AOP實現的。
大多數Spring用戶選擇聲明式事務管理。這是最少影響應用代碼的選擇, 因而這是和非侵入性的輕量級容器的觀念是一致的。從考慮EJB CMT和Spring聲明式事務管理的相似以及不同之處出發是很有益的。 它們的基本方法是相似的:都可以指定事務管理到單獨的方法;如果需要可以在事務上 下文調用setRollbackOnly()方法。不同之處如下:
-
不象EJB CMT綁定在JTA上,Spring聲明式事務管理可以在任何環境下使用。 只需更改配置文件,它就可以和JDBC、JDO、Hibernate或其他的事務機制一起工作
-
Spring可以使聲明式事務管理應用到普通Java對象,不僅僅是特殊的類,如EJB
-
Spring提供聲明式回滾規則:EJB沒有對應的特性, 我們將在下面討論這個特性。回滾可以聲明式控制,不僅僅是編程式的
-
Spring允許你通過AOP定制事務行為。例如,如果需要,你可以在事務 回滾中插入定制的行為。你也可以增加任意的通知,就象事務通知一樣。使用 EJB CMT,除了使用setRollbackOnly(),你沒有辦法能 夠影響容器的事務管理
-
Spring不提供高端應用服務器提供的跨越遠程調用的事務上下文傳播。如 果你需要這些特性,我們推薦你使用EJB。然而,不要輕易使用這些特性。通常我 們并不希望事務跨越遠程調用
回滾規則的概念是很重要的:它們使得我們可以指定哪些異常應該發起自 動回滾。我們在配置文件中,而不是Java代碼中,以聲明的方式指定。因此,雖然我們仍 然可以編程調用TransactionStatus對象的?setRollbackOnly()方法來回滾當前事務,多數時候我們可以 指定規則,如MyApplicationException應該導致回滾。 這有顯著的優點,業務對象不需要依賴事務基礎設施。例如,它們通常不需要引 入任何Spring API,事務或其他任何東西。
EJB的默認行為是遇到系統異常(通常是運行時異常), EJB容器自動回滾事務。EJB CMT遇到應用程序異常?(除了java.rmi.RemoteException外的checked異常)時不 會自動回滾事務。雖然Spring聲明式事務管理沿用EJB的約定(遇到unchecked 異常自動回滾事務),但是這是可以定制的。
按照我們的測試,Spring聲明式事務管理的性能要勝過EJB CMT。
通常通過TransactionProxyFactoryBean設置Spring事務代理。我們需 要一個目標對象包裝在事務代理中。這個目標對象一般是一個普通Java對象的bean。當我 們定義TransactionProxyFactoryBean時,必須提供一個相關的 PlatformTransactionManager的引用和事務屬性。?事務屬性含有上面描述的事務定義。
<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>事務代理會實現目標對象的接口:這里是id為petStoreTarget的bean。(使用 CGLIB也可以實現具體類的代理。只要設置proxyTargetClass屬性為true就可以。 如果目標對象沒有實現任何接口,這將自動設置該屬性為true。通常,我們希望面向接口而不是 類編程。)使用proxyInterfaces屬性來限定事務代理來代 理指定接口也是可以的(一般來說是個好想法)。也可以通過從?org.springframework.aop.framework.ProxyConfig繼承或所有AOP代理工廠共享 的屬性來定制TransactionProxyFactoryBean的行為。
這里的transactionAttributes屬性定義在?org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource?中的屬性格式來設置。這個包括通配符的方法名稱映射是很直觀的。注意 insert*的映射的值包括回滾規則。添加的-MyCheckedException?指定如果方法拋出MyCheckedException或它的子類,事務將 會自動回滾。可以用逗號分隔定義多個回滾規則。-前綴強制回滾,+前綴指定提 交(這允許即使拋出unchecked異常時也可以提交事務,當然你自己要明白自己 在做什么)。
TransactionProxyFactoryBean允許你通過 “preInterceptors”和“postInterceptors”屬性設置“前”或“后”通知來提供額外的 攔截行為。可以設置任意數量的“前”和“后”通知,它們的類型可以是?Advisor(可以包含一個切入點),?MethodInterceptor或被當前Spring配置支持的通知類型 (例如ThrowAdvice,?AfterReturningtAdvice或BeforeAdvice, 這些都是默認支持的)。這些通知必須支持實例共享模式。如果你需要高級AOP特 性來使用事務,如有狀態的maxin,那最好使用通用的?org.springframework.aop.framework.ProxyFactoryBean, 而不是TransactionProxyFactoryBean實用代理創建者。
也可以設置自動代理:配置AOP框架,不需要單獨的代理定義類就可以生成類的 代理。
更多信息和實例請參閱AOP章節。
無論你是是否是AOP專家,都可以更有效地使用Spring的 聲明式事務管理。但是,如果你想成為Spring AOP的高級用戶,你會發現整合聲明 式事務管理和強大的AOP性能是非常容易的。7.4.1.?BeanNameAutoProxyCreator,另一種聲明方式
TransactionProxyFactoryBean非常有用,當事 務代理包裝對象時,它使你可以完全控制代理。如果你需要用一致的方式(例如,一個 樣板文件,“使所有的方法事務化”)包裝大量的bean,使用一個?BeanFactoryPostProcessor的一個實現,?BeanNameAutoProxyCreator,可以提供另外一種方法, 這個方法在這種簡單的情況下更加簡單。
重述一下,一旦ApplicationContext讀完它的初始化信息,它將初始化所有實 現BeanPostProcessor接口的bean,并且讓它們后處理 ApplicationContext中所有其他的bean。所以使用這種機制,一個正 確配置的BeanNameAutoProxyCreator可以用來后處 理所有ApplicationContext中所有其他的bean(通過名稱來識別),并且把它 們用事務代理包裝起來。真正生成的事務代理和使用?TransactionProxyFactoryBean生成的基本一致,這里不再 討論。
讓我們看下面的配置示例:
<!-- Transaction Interceptor set up to do PROPOGATION_REQUIRED on all methods --><bean id="matchAllWithPropReq" class="org.springframework.transaction.interceptor.MatchAlwaysTransactionAttributeSource"><property name="transactionAttribute"><value>PROPAGATION_REQUIRED</value></property></bean><bean id="matchAllTxInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"><property name="transactionManager"><ref bean="transactionManager"/></property><property name="transactionAttributeSource"><ref bean="matchAllWithPropReq"/></property></bean><!-- One BeanNameAutoProxyCreator handles all beans where we want all methods to use PROPOGATION_REQUIRED --><bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"><property name="interceptorNames"><list><idref local="matchAllTxInterceptor"/><idref bean="hibInterceptor"/></list></property><property name="beanNames"><list><idref local="core-services-applicationControllerSevice"/><idref local="core-services-deviceService"/><idref local="core-services-authenticationService"/><idref local="core-services-packagingMessageHandler"/><idref local="core-services-sendEmail"/><idref local="core-services-userService"/></list></property></bean>假設我們在ApplicationContext中已經有一個TransactionManager的實例 ,我們首先要做的使創建一個?TransactionInterceptor實例。 根據通過屬性傳遞的TransactionAttributeSource接口的一個實現,?ransactionInterceptor決定哪個 方法被攔截。這個例子中,我們希望處理匹配 所有方法這種最簡單的情況。這個不是最有效的方式,但設置非常迅速,因為 我可以使用預先定義的匹配所有方法的?MatchAlwaysTransactionAttributeSource類。如果我 們需要特定的方式,可以使用?MethodMapTransactionAttributeSource,?NameMatchTransactionAttributeSource或AttributesTransactionAttributeSource。
現在我們已經有了事務攔截器,我們只需把它交給我們定義的?BeanNameAutoProxyCreator實例中,這樣AppliactonContext中 定義的6個bean以同樣的方式被封裝。你可以看到,這 比用TransactionProxyFactoryBean以一種方式單獨封裝6個bean簡潔很 多。封裝第7個bean只需添加一行配置。
你也許注意到可以應用多個攔截器。在這個例子中,我們還應用了一個 前面定義的HibernateInterceptor?(bean id=hibInterceptor),它為我們管理 Hibernare的會話。
有一件事值得注意,就是在TransactionProxyFactoryBean, 和BeanNameAutoProxyCreator切換時bean的命名。 第一種情況,你只需給你想要包裝的bean一個類似myServiceTarget的id, 給代理對象一個類似myService的id,然后所有代理對象的用戶只需引用代理對象, 如myService(這些是通用命名規范, 要點是目標對象要有和代理對象不同的名稱,并且它們都要在ApplicationContext中可用)。然而, 使用BeanNameAutoProxyCreator時, 你得命名目標對象為myService。 然后當BeanNameAutoProxyCreator后處理目標對象 并生成代理時,它使得代理以和原始對象的名稱被插入到 ApplicationContext中。從這一點看,只有代理(含有被包裝的對象)在ApplicationContext中可用。
7.5.?編程式還是聲明式事務管理
如果你只有很少的事務操作,使用編程式事務管理才是個好主意。例如, 如果你有一個WEB應用需要為某些更新操作提供事務,你可能不想用Spring或其 他技術設置一個事務代理。使用?TransactionTemplate可能是個很好的方法。
另一方面,如果你的應用有大量的事務操作,聲明式事務管理就很有價值。它使得事務管理從業務邏輯分離, 并且Spring中配置也不困難。使用Spring,而不是EJB CMT,聲明式事務管理配置的成本極大地降低。
7.6.?你需要應用服務器管理事務嗎?
Spring的事務管理能力--尤其聲明式事務管理--極大地改變了J2EE應用程序需要 應用服務器的傳統想法。
尤其,你不需要應用服務器僅僅為了通過EJB聲明事務。事實上,即使你擁 有強大JTA支持的應用服務器,你也可以決定使用Spring聲明式事務管理提供比 EJB CMT更強大更高效的編程模型。
只有需要支持多個事務資源時,你才需要應用服務器的JTA支持。許多應用沒有 這個需求。例如許多高端應用使用單一的,具有高度擴展性的數據庫,如Oracle 9i RAC。
當然也許你需要應用服務器的其它功能,如JMS和JCA。但是如果你只需使用 JTA,你可以考慮開源的JTA實現,如JOTM(Spring整合了JOTM)。但是, 2004年早期,高端的應用服務器提供更健壯的XA資源支持。
最重要一點,使用Spring,你可以選擇何時將你的應用遷移到完整 應用服務器。使用EJB CMT或JTA都必須書寫代碼使用局部事務, 例如JDBC連接的事務,如果以前需要全局的容器管理的事務,還要面臨著繁重 的改寫代碼的過程,這些日子一去不復返了。使用Spring只有配置需要改變,你的代碼 不需要修改。
7.7.?公共問題
開發著需要按照需求仔細的使用正確的?PlatformTransactionManager實現。
理解Spring事務抽象時如何和JTA全局事務一起工作是非常重要的。使用得當, 就不會有任何沖突:Spring僅僅提供一個簡單的,可以移植的抽象層。
如果你使用全局事務,你必須為你的所有事務操作使用Spring的?org.springframework.transaction.jta.JtaTransactionManager。 否則Spring將試圖在象容器數據源這樣的資源上執行局部事務。這樣的局部事務沒有任何 意義,好的應用服務器會把這作為一個錯誤。
from:?http://docs.huihoo.com/spring/zh-cn/transaction.html
總結
以上是生活随笔為你收集整理的Spring - Java/J2EE Application Framework 应用框架 第 7 章 事务管理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring - Java/J2EE A
- 下一篇: java美元兑换,(Java实现) 美元