?
本文檔使用 php中文網(wǎng)手冊 發(fā)布
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框架的哲學,PlatformTransactionManager
是一個 接口。因而如果需要它可以很容易地被模擬和樁化。它也沒有和一個查找策略如JNDI捆綁在一起:PlatformTransactionManager
的實現(xiàn)定義和其他Spring IoC容器中的對象一樣。這個好處使得即使使用JTA,也是一個很有價值的抽象:事務代碼可以比直接使用JTA更加容易測試。
繼續(xù)Spring哲學,可由任何 PlatformTransactionManager
的接口方法拋出的 TransactionException
是unchecked exception(繼承自java.lang.RuntimeException
)的。底層的事務失敗幾乎總是致命的。很少情況下應用程序代碼可以從它們中恢復,不過應用開發(fā)者依然可以捕獲并處理TransactionException
,他們可以自由決定怎么干。
getTransaction(..)
方法根據(jù)一個類型為 TransactionDefinition
的參數(shù)返回一個 TransactionStatus
對象。返回的 TransactionStatus
對象可能代表一個新的或已經(jīng)存在的事務(如果在當前調(diào)用堆棧有一個符合條件的事務。如同J2EE事務環(huán)境,一個 TransactionStatus
也是和執(zhí)行 線程 綁定的)。
TransactionDefinition
接口指定:
事務隔離:當前事務和其它事務的隔離的程度。例如,這個事務能否看到其他事務未提交的寫數(shù)據(jù)?
事務傳播:通常在一個事務中執(zhí)行的所有代碼都會在這個事務中運行。但是,如果一個事務上下文已經(jīng)存在,有幾個選項可以指定一個事務性方法的執(zhí)行行為:例如,簡單地在現(xiàn)有的事務中繼續(xù)運行(大多數(shù)情況);或者掛起現(xiàn)有事務,創(chuàng)建一個新的事務。Spring提供EJB CMT中常見的事務傳播選項。
事務超時: 事務在超時前能運行多久(自動被底層的事務基礎設施回滾)。
只讀狀態(tài): 只讀事務不修改任何數(shù)據(jù)。只讀事務在某些情況下(例如當使用Hibernate時),是一種非常有用的優(yōu)化。
這些設置反映了標準概念。如果需要,請查閱討論事務隔離層次和其他核心事務概念的資源:理解這些概念在使用Spring框架和其他事務管理解決方案時是非常關鍵的。
TransactionStatus
接口為處理事務的代碼提供一個簡單的控制事務執(zhí)行和查詢事務狀態(tài)的方法。這個概念應該是熟悉的,因為它們在所有的事務API中是相同的:
public interface TransactionStatus { boolean isNewTransaction(); void setRollbackOnly(); boolean isRollbackOnly(); }
使用Spring時,無論你選擇編程式還是聲明式的事務管理,定義一個正確的 PlatformTransactionManager
實現(xiàn)都是至關重要的。按照Spring的良好風格,這種重要定義都是通過IoC實現(xiàn)的。
一般來說,選擇PlatformTransactionManager
實現(xiàn)時需要知道當前的工作環(huán)境,如JDBC、JTA、Hibernate等。下面的例子來自Spring示例應用――jPetStore――中的dataAccessContext-local.xml
文件,其中展示了一個局部PlatformTransactionManager
實現(xiàn)是怎么定義的(僅限于純粹JDBC環(huán)境)
我們必須先定義一個JDBC DataSource
,然后使用Spring的DataSourceTransactionManager
,并傳入指向DataSource
的引用。
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </bean>
PlatformTransactionManager
bean的定義如下:
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
如果我們在J2EE容器里使用JTA,就像示例中 'dataAccessContext-jta.xml'
文件所示,我們將通過JNDI和Spring的 JtaTransactionManager
來獲取一個容器管理的 DataSource
。JtaTransactionManager
不需要知道 DataSource
和其他特定的資源,因為它將使用容器提供的全局事務管理。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.0.xsd">
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/jpetstore"/>
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" />
<!-- other <bean/>
definitions here -->
</beans>
上面 'dataSource'
的bean定義使用了 'jee'
名稱空間下的 <jndi-lookup/>
標簽。想了解更多的配置信息, 請看 附錄?A, XML Schema-based configuration
,關于 <jee/>
標簽的信息,可參考 第?A.2.3?節(jié) “The jee
schema” 節(jié)。
我們也可以很容易地使用Hibernate局部事務,就像下面的Spring框架的 PetClinic 示例應用中的例子一樣)。這種情況下,我們需要定義一個Hibernate的 LocalSessionFactoryBean
,應用程序從中獲取到Hibernate Session
實例。
DataSource
的bean定義同上例類似(這里不再展示)。不過,如果是一個JEE容器提供的 DataSource
,它將由JEE容器自身,而不是Spring框架來管理事務。
這種情況中'txManager'
bean的類型為 HibernateTransactionManager
。同樣地,DataSourceTransactionManager
需要一個指向 DataSource
的引用,而 HibernateTransactionManager
需要一個指向 SessionFactory
的引用。
<bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="mappingResources"> <list> <value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <value> hibernate.dialect=${hibernate.dialect} </value> </property> </bean> <bean id="txManager" class="org.springframework.orm.hibernate.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean>
我們可以簡單地使用 JtaTransactionManager
來處理Hibernate事務和JTA事務,就像我們處理JDBC,或者任何其它的資源策略一樣。
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
注意任何資源的JTA配置都是這樣的,因為它們都是全局事務,可以支持任何事務性資源。
在所有這些情況下,應用程序代碼根本不需要做任何改動。我們僅僅通過改變配置就可以改變事務管理方式,即使這些更改是在局部事務和全局事務間切換。