Development

Proper XA Transactions with Oracle and Spring

There are a load of places on the internet that give partial solutions to this common problem: XA Transactions, outside an application container. Here’s an attempt to put them all in one place.

Context

This uses the following technologies.

Oracle
Bitronix Transaction Manager
Spring

Selecting the correct oracle drivers

You will need to use the 11.1.0.6.0 11g drivers. These are suitable for previous versions of oracle.

Previous 10.x version of the drivers have a connetion/resource leak which will bite when you use XA. Configuring Oracle for XA Transactions

Oracle out-of-the-box does not come configured suitable for XA transactions. You need to run the following as a SYS user.

grant select on pending_trans$ to <user>;
grant select on dba_2pc_pending to <user>;
grant select on dba_pending_transactions to <user>;
grant execute on dbms_system to <user>;

Setting up the transaction manager

<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="transactionManager" ref="bitronixTransactionManager"/>
        <property name="userTransaction" ref="bitronixTransactionManager"/>
    </bean>
 
    <bean id="btmConfig" factory-method="getConfiguration" class="bitronix.tm.TransactionManagerServices">
        <property name="serverId" value="spring-btm"/>
    </bean>
 
    <bean id="bitronixTransactionManager" factory-method="getTransactionManager"
          class="bitronix.tm.TransactionManagerServices" depends-on="transactionManagerConfiguration,myDataSource"
          destroy-method="shutdown"/>

Setting up the data source

<bean id="myDataSource" class="bitronix.tm.resource.jdbc.PoolingDataSource" init-method="init" destroy-method="close">
        <property name="className" value="oracle.jdbc.xa.client.OracleXADataSource"/>
        <property name="uniqueName" value="myOracleDataSource"/>
        <property name="minPoolSize" value="0"/>
        <property name="maxPoolSize" value="5"/>
        <property name="allowLocalTransactions" value="true"/>
        <property name="testQuery" value="select sysdate from dual"/>
        <property name="driverProperties">
            <props>
                <prop key="user">${db.username}</prop>
                <prop key="password">${db.password}</prop>
                <prop key="URL">jdbc:oracle:thin:@${db.host}:${db.port}:${db.sid}</prop>
            </props>
        </property>
    </bean>

Sweet!

Actually that’s it.

The “allowLocalTransactions” setting means that you can do stuff outside a global transaction. This isn’t really recommended, but will work.

Using all this inside tests.

You must remember to destroy your application context each time you create it. Otherwise the transaction manager will grumble at being created multiple times.

public class FooTest {
 
  private ApplicationContext context;
 
  @Before
  public void setup() throws Exception {
   context = createMyLovelyContext();
  }
 
   @After
  public void teardown() throws Exception {
    context.close();
  }
}
comments powered by Disqus