JTA事務管理--配置剖析


概述 
   【IT168 專稿】Spring 通過AOP技術可以讓我們在脫離EJB的情況下享受聲明式事務的豐盛大餐,脫離Java EE應用服務器使用聲明式事務的道路已經暢通無阻。但是很大部分人都還認為脫離Java EE應用服務器就無法使用JTA事務,這是一個誤解。其實,通過配合使用ObjectWeb的JOTM開源項目,不需要Java EE應用服務器,Spring也可以提供JTA事務。 

    正因為AOP讓Spring擁有了脫離EJB容器的聲明式事務能力,而JOTM讓我們在脫離Java EE應用服務器下擁有JTA事務能力。所以,人們將AOP和JOTM稱為Java軟件開發的兩個聖杯。 

    本文將講解Spring在不同環境下提供JTA事務的配置過程,這包括:Spring中直接集成JOTM提供JTA事務管理、將JOTM集成到Tomcat中,Spring通過引用Tomcat JNDI數據源提供JTA事務管理、引用其它功能完善JavaEE應用服務器所提供的JTA事務管理。 

    通過集成JOTM,直接在Spring中使用JTA事務 
    JOTM(Java Open Transaction Manager)是ObjectWeb的一個開源JTA實現,它本身也是開源應用程序服務器JOnAS(Java Open Application Server)的一部分,為其提供JTA分布式事務的功能。 

    Spring 2.0附帶的依賴類庫中雖然包含jotm類庫,但是並不完整,你可以到http://jotm.objectweb.org下載完全版的JOTM。 
Spring為JOTM提供了一個org.springframework.transaction.jta.JotmFactoryBean支持類,通過該支持類可以方便地創建JOTM本地實例。 

   下面,我們通過配置,使上節中BbtForumImpl#addTopic()方法工作在JTA事務的環境下。addTopic()內部使用兩個DAO類(TopicDao和PostDao)分別訪問不同數據庫中的表。通過下面的步驟說明了使addTopic()方法擁有JTA事務的整個過程: 

    1. 將JOTM以下類庫添加到類路徑中: 
    jotm.jar 
    xapool.jar 
    jotm_jrmp_stubs.jar 
    jta-spec1_0_1.jar 
    connector-1_5.jar 

   2. 編寫JOTM配置文件,放到類路徑下 
    carol.properties 
    #JNDI調用協議 
    carol.protocols=jrmp 
    #不使用CAROL JNDI封裝器 
    carol.start.jndi=false 
    #不啟動命名服務器 
    carol.start.ns=false

3. 在MySQL上建立兩個數據庫 
    在MySQL數據庫中運行SQL腳本,建立topicdb和postdb兩個數據庫,在topicdb數據庫中創建t_topic表,在postdb數據庫中創建t_post表。我們希望在這兩個數據庫上進行JTA事務。

4. 在Spring配置文件中配置JOTM 
    代碼清單 1 applicationContext-jta.xml 
    … 

<!--①JOTM本地實例 -->
<bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean" />
<!--②JTA事務管理器 -->
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"> 
    <property name="userTransaction" ref="jotm" /> <!--②-1:指定userTransaction屬性--> 
</bean> 
<!--③XAPool配置,內部包含了一個XA數據源,對應topicdb數據庫--> 
<bean id="topicDS" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown"> 
    <property name="dataSource"> 
        <!--③-1:內部XA數據源 -->
        <bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown"> 
            <property name="transactionManager" ref="jotm" /> 
            <property name="driverName" value="com.MySQL.jdbc.Driver" /> 
            <property name="url" value="jdbc:MySQL://localhost:3309/topicdb" /> 
        </bean> 
    </property> 
    <property name="user" value="root" /> 
    <property name="password" value="1234" /> 
</bean> 
<!--④按照③相似的方式配置另一個XAPool,對應postdb數據庫, -->
<bean id="postDS" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource"  destroy-method="shutdown"> 
    <property name="dataSource"> 
        <bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown"> 
            <property name="transactionManager" ref="jotm" /> 
            <property name="driverName" value="com.mysql.jdbc.Driver" /> 
            <property name="url" value="jdbc:mysql://localhost:3309/postdb" /> 
        </bean> 
    </property> 
    <property name="user" value="root" /> 
    <property name="password" value="1234" />
 </bean> 
 <!--⑤配置訪問topicDB數據源的Spring JDBC模板 -->
 <bean id="topicTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> 
    <property name="dataSource" ref="topicDS" /> 
 </bean> 
 <!--⑥配置訪問postDB數據源的Spring JDBC模板 -->
 <bean id="postTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> 
    <property name="dataSource" ref="postDS" /> 
 </bean> 
 <!--⑦基於topicTemplate數據源的topicDao -->
 <bean id="topicDao" class="com.baobaotao.dao.jdbc.TopicJdbcDao"> 
    <property name="jdbcTemplate" ref="topicTemplate" /> 
 </bean> 
 <!--⑧基於postTemplate數據源的postDao -->
 <bean id="postDao" class="com.baobaotao.dao.jdbc.PostJdbcDao"> 
    <property name="jdbcTemplate" ref="postTemplate" /> 
 </bean> 
 <!--⑨進行跨數據庫JTA事務的業務類-->
<bean id="bbtForum" class="com.baobaotao.service.impl.BbtForumImpl"> 
    <property name="topicDao" ref="topicDao" /> 
    <property name="postDao" ref="postDao" /> 
</bean> 
<!--⑩對BbtForumImpl業務類中的@Transaction注解進行驅動,以織入事務管理切面 -->
<tx:annotation-driven transaction-manager="txManager" />

    首先,我們在①處通過Spring所提供的JotmFactoryBean創建一個本地JOTM實例,該實例同時實現了   javax.transaction.UserTransaction和javax.transaction.TransactionManager接口,它可以和ObjectWeb的XAPool一起工作。 
JTA事務管理器通過userTransaction屬性引用本地JOTM實例,Spring的JtaTransactionManager會自動探測到傳入的javax.transaction.UserTransaction引用也實現了javax.transaction.TransactionManager,所以我們無需再配置JtaTransactionManager的transactionManager屬性,如②所示。 
     在Spring中配置JOTM的另一個關鍵問題是配置XAPool,支持JTA事務的數據源必須封裝成XAPool。首先,我們通過org.enhydra.jdbc.standard.StandardXADataSource 配置一個XA數據源,它指向topicdb數據庫,如③-1所示。而后,通過org.enhydra.jdbc.pool.StandardXAPoolDataSource將其封裝成一個XAPool,如③所示。按照相同的方式,配置指向postdb數據庫的XAPool,如④所示。 
     接下來的配置就順理成章了,分別使用Spring JDBC的模板類配置DAO類,然后再配置引用DAO類的業務類。關於Spring JDBC的詳細內容,參見第10章的內容。 
     這里,我們使用@Transaction注解對業務類BbtForumImpl進行事務聲明,所以通過<tx:annotation-driven/>對此進行驅動,BbtForumImpl的代碼如下所示: 
代碼清單 2 BbtForumImpl 

package com.baobaotao.service.impl; 
import org.springframework.transaction.annotation.Transactional; 
import com.baobaotao.dao.PostDao; 
import com.baobaotao.dao.TopicDao; 
import com.baobaotao.domain.Forum; 
import com.baobaotao.domain.Topic; 
import com.baobaotao.service.BbtForum; 
@Transactional// ①事務注解,以便Spring動態織入事務管理功能
public class BbtForumImpl implements BbtForum { 
    private TopicDao topicDao; 
    private PostDao postDao; 
    
    public void addTopic(Topic topic) throws Exception { 
        //②將方法將被施加JTA事務的增強 
        topicDao.addTopic(topic); 
        postDao.addPost(topic.getPost()); 
    } 
}

    BbtForumImpl將Dao類組織起來,PostDao和TopicDao分別訪問不同數據庫中表,通過Spring注解驅動事務切面的增強后,它們將工作於同一個JTA事務中。 

    5. 在Spring中運行測試 
    代碼清單 3 TestBbtForumJta 

package com.baobaotao.service; 
import org.springframework.test.AbstractDependencyInjectionSpringContextTests;

public class TestBbtForumJta extends AbstractDependencyInjectionSpringContextTests{ 

    private BbtForum bbtForum; 
    private final Logger logger = Logger.getLogger(getClass()); 
    
    public void setBbtForum(BbtForum bbtForum) { 
        this.bbtForum = bbtForum; 
    } 

    protected String[] getConfigLocations() { 
        return new String[]{"classpath:applicationContext-jta.xml"}; 
    } 
    
    public void testAddPost() throws Exception{ 
        logger.info("begin........"); 
        Topic topic = new Topic(); 
        topic.setTopicTitle("Title -pfb"); 
        Post post = new Post(); 
        post.setPostText("post content -pfb"); 
        topic.setPost(post); 
        bbtForum.addTopic(topic); 
        //①使用了JTA事務的業務方法 
        logger.info("end........"); 
    } 
}

    通過Spring測試類AbstractDependencyInjectionSpringContextTests的支持,很容易編寫一個測試類,對啟用了JTA事務的BbtForum#addTopic()方法進行測試。建議你將Log4J設置為DEBUG,這樣就可以通過豐富的輸出日志觀測到JTA事務的執行情況。運行這個測試類后,你將可以看到JTA事務被正確實施。 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM