JPA & XA

After I wrote a blog about Spring & JPA settings: http://jellyfish.iteye.com/admin/blogs/899281, a friend asked me a question about XA on JPA.

 

Last time I did XA was the year 2002, so I figured maybe it's time to revisit this topic to see how things have changed since then. Well, a lot, frankly.

 

I did XA across two data sources, db2 and sybase; and across a data source and a JMS queue. I am using Spring 3 now, the first thing that I noticed is that for some unknown reason, Spring 3 drop the JOTM support (Spring 2.5 had it). Does anyone know the reason behind it?

 

So I am going to use Atomikos's libs for XA. I started from the above blog to see how much I need to change in order to get XA working.

 

First, the persistence file needs a little change (marked in red):

 

<?xml version="1.0" encoding="UTF-8" ?>

<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
	version="2.0" xmlns="http://java.sun.com/xml/ns/persistence">

    <persistence-unit name="myPersistenceUnit" transaction-type="JTA">
        <mapping-file>my/test/dao/jpa/hibernate/book.jpa.hibernate.xml</mapping-file>
    </persistence-unit>
</persistence>

 I rename this file to persistence.jta.xml, since we need to reference this file later. Next, we need to change the Spring xml file, since the change is pretty dramatic, I just paste the entire file and then explain the beans one by one later:

<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <bean id="bookService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <property name="transactionManager" ref="springTransactionManager"/>
        <property name="target" ref="bookServiceTarget"/>
        <property name="transactionAttributes">
            <props>
                <prop key="*">PROPAGATION_REQUIRED, -Exception</prop>
            </props>
        </property>
    </bean>

    <bean id="userTransactionService"
          class="com.atomikos.icatch.config.UserTransactionServiceImp"
          init-method="init" destroy-method="shutdownForce">
        <constructor-arg>
            <props>
                <prop key="com.atomikos.icatch.service">
                    com.atomikos.icatch.standalone.UserTransactionServiceFactory
                </prop>
            </props>
        </constructor-arg>
    </bean>

    <bean id="springTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="transactionManager">
            <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
                  init-method="init" destroy-method="close" depends-on="userTransactionService">
                <property name="forceShutdown" value="false"/>
                <property name="startupTransactionService" value="false"/>
            </bean>
        </property>
        <property name="userTransaction">
            <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp" depends-on="userTransactionService">
                <property name="transactionTimeout" value="5"/>
            </bean>
        </property>
        <property name="allowCustomIsolationLevels" value="true"/>
    </bean>

    <bean id="bookServiceTarget" class="my.test.dao.jpa.hibernate.BookService">
        <property name="dao1" ref="bookDao1"/>
        <property name="dao2" ref="bookDao2"/>
    </bean>
    
    <bean id="bookDao1" class="my.test.dao.jpa.hibernate.BookDaoJpaHibernateJtaImpl">
        <property name="entityManager">
            <bean class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
                <property name="entityManagerFactory" ref="myEmf1"/>
            </bean>
        </property>
    </bean>

    <bean id="bookDao2" class="my.test.dao.jpa.hibernate.BookDaoJpaHibernateJtaImpl">
        <property name="entityManager">
            <bean class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
                <property name="entityManagerFactory" ref="myEmf2"/>
            </bean>
        </property>
    </bean>

    <bean id="myEmf1" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="myPersistenceUnit"/>
        <property name="dataSource" ref="mydataSource1"/>
        <property name="persistenceXmlLocation" value="classpath:my/test/dao/jpa/hibernate/persistence.jta.xml"/>
        <property name="loadTimeWeaver">
            <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
        </property>
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <!-- <property name="showSql" value="true"/> -->
                <property name="generateDdl" value="false"/>
                <property name="database" value="ORACLE"/>
                <property name="databasePlatform" value="org.hibernate.dialect.OracleDialect"/>
            </bean>
        </property>
        <property name="jpaProperties">
            <map>
                <entry key="hibernate.transaction.manager_lookup_class"
                       value="com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup"/>
            </map>
        </property>
    </bean>

    <bean id="myEmf2" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="myPersistenceUnit"/>
        <property name="dataSource" ref="mydataSource2"/>
        <property name="persistenceXmlLocation" value="classpath:my/test/dao/jpa/hibernate/persistence.jta.xml"/>
        <property name="loadTimeWeaver">
            <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
        </property>
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <!-- <property name="showSql" value="true"/> -->
                <property name="generateDdl" value="false"/>
                <property name="database" value="ORACLE"/>
                <property name="databasePlatform" value="org.hibernate.dialect.OracleDialect"/>
            </bean>
        </property>
        <property name="jpaProperties">
            <map>
                <entry key="hibernate.transaction.manager_lookup_class"
                       value="com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup"/>
            </map>
        </property>       
    </bean>

    <bean id="mydataSource1" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
        <property name="xaDataSourceClassName" value="oracle.jdbc.xa.client.OracleXADataSource"/>
        <property name="uniqueResourceName" value="mydataSource1"/>
        <property name="poolSize" value="1"/>
        <property name="xaProperties">
            <props>
                <prop key="URL">jdbc:oracle:thin:@***</prop>
                <prop key="user">***</prop>
                <prop key="password">***</prop>
            </props>
		</property>
    </bean>

    <bean id="mydataSource2" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
        <property name="xaDataSourceClassName" value="oracle.jdbc.xa.client.OracleXADataSource"/>
        <property name="uniqueResourceName" value="mydataSource2"/>
        <property name="poolSize" value="1"/>
        <property name="xaProperties">
            <props>
                <prop key="URL">jdbc:oracle:thin:@***</prop>
                <prop key="user">***</prop>
                <prop key="password">***</prop>
            </props>
		</property>
    </bean>    
</beans>

 Let's go through this from bottom up. The last two beans are (different) data sources (I am using Oracle), make sure you use XA jdbc drivers. We want to test out the transaction management across two different databases (could be oracle/sybase/db2/sqlserver).

 

The next two beans are entity manager factor (emf). We first changed the persistence.xml new path, then add a "jpaProperties" entry according to Atomikos's document:

http://www.atomikos.com/Documentation/SpringIntegration

http://www.atomikos.com/Documentation/HibernateIntegration

 

Now the DAO class is little bit problematic since the emf was injected through @PersistenceContext. Let's remove this annotation and create a setter, like this:

public class BookDaoJpaHibernateJtaImpl  implements BookDao
{
    private EntityManager em;

    public void setEntityManager(EntityManager em)
    {
        this.em = em;
    }

    // other methods are the same
}

 So the next two DAO beans (bookDao1 and bookDao2) use this class. The emf fields are injected separately.

 

Next, we create a new class that uses the above two DAO classes:

package my.test.dao.jpa.hibernate;

import my.test.Book;
import my.test.BookDao;

public class BookService
{
    private BookDao dao1;
    private BookDao dao2;

    public void save(Book book)
    {

        dao2.save(book);
        dao1.save(book);
    }

    public void setDao2(BookDao dao2)
    {
        this.dao2 = dao2;
    }

    public void setDao1(BookDao dao1)
    {
        this.dao1 = dao1;
    }
}

 and we have the corresponding bean bookServiceTarget.

 

The next two beans are Atomikos related, together they define the transaction manager. Please check Atomikos document.

 

Finally, the first bean bookService stitch the bookServiceTarget and the transaction manager together.

 

During testing, I created two same tables in each database, add an unique index for book_id on the table in the second database. Then insert a simple book object through book service. The first run should be fine since both database tables are empty. The second run should fail because the same id

was tried to be inserted into the table in the second database. After the second run, both tables should have count 1 on rows (i.e., the second run got rolled back).

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值