What I need to do is a distributed transaction over three distinct Oracle databases. One of each must be accessed through JDBC, the two others through Hibernate. Here is my Atomikos configuration :
<bean id="mainDataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean"
init-method="init" destroy-method="close">
<property name="xaDataSourceClassName" value="${mainDataSource.jdbc.className}" />
<property name="uniqueResourceName" value="${mainDataSource.jdbc.uniqueName}" />
<property name="poolSize" value="${mainDataSource.jdbc.maxPoolSize}" />
<property name="testQuery" value="${mainDataSource.jdbc.testQuery}" />
<property name="xaProperties">
<props>
<prop key="URL">${mainDataSource.jdbc.url}</prop>
<prop key="user">${mainDataSource.jdbc.user}</prop>
<prop key="password">${mainDataSource.jdbc.password}</prop>
</props>
</property>
</bean>
<bean id="optionalDataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean"
init-method="init" destroy-method="close">
<property name="xaDataSourceClassName" value="${optionalDataSource.jdbc.className}" />
<property name="uniqueResourceName" value="${optionalDataSource.jdbc.uniqueName}" />
<property name="poolSize" value="${optionalDataSource.jdbc.maxPoolSize}" />
<property name="testQuery" value="${optionalDataSource.jdbc.testQuery}" />
<property name="xaProperties">
<props>
<prop key="URL">${optionalDataSource.jdbc.url}</prop>
<prop key="user">${optionalDataSource.jdbc.user}</prop>
<prop key="password">${optionalDataSource.jdbc.password}</prop>
</props>
</property>
</bean>
<bean id="eventDataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean"
init-method="init" destroy-method="close">
<property name="xaDataSourceClassName" value="${eventDataSource.jdbc.className}" />
<property name="uniqueResourceName" value="${eventDataSource.jdbc.uniqueName}" />
<property name="poolSize" value="${eventDataSource.jdbc.maxPoolSize}" />
<property name="testQuery" value="${eventDataSource.jdbc.testQuery}" />
<property name="xaProperties">
<props>
<prop key="URL">${eventDataSource.jdbc.url}</prop>
<prop key="user">${eventDataSource.jdbc.user}</prop>
<prop key="password">${eventDataSource.jdbc.password}</prop>
</props>
</property>
</bean>
<bean id="atomikosTransactionService" 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>
<prop key="com.atomikos.icatch.tm_unique_name">${transactionmanager.atomikos.tmId}</prop>
<prop key="com.atomikos.icatch.enable_logging">${transactionmanager.atomikos.enablelogging}</prop>
<prop key="com.atomikos.icatch.output_dir">${transactionmanager.atomikos.console}</prop>
<prop key="com.atomikos.icatch.log_base_dir">${transactionmanager.atomikos.tmLog}</prop>
<prop key="com.atomikos.icatch.log_base_name">${transactionmanager.atomikos.tmLogBaseName}</prop>
</props>
</constructor-arg>
</bean>
<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp"
depends-on="atomikosTransactionService">
<property name="transactionTimeout" value="300" />
</bean>
<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
init-method="init" depends-on="atomikosTransactionService"
destroy-method="close">
<!-- when close is called, should we force transactions to terminate or
not? -->
<property name="forceShutdown" value="true" />
<property name="startupTransactionService" value="false" />
</bean>
<bean id="mainTransactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="atomikosTransactionManager" />
<property name="userTransaction" ref="atomikosUserTransaction" />
</bean>
<!-- Der mainTransactionManager ist der Default-TransactionManager von Spring. -->
<alias name="mainTransactionManager" alias="transactionManager" />
The Hibernate configuration is inspired by the solution found on this topic :
<!-- inject the Atomikos transaction manager into a Spring Hibernate adapter
for JTA Platform -->
<bean id="springJtaPlatformAdapter"
class="my.domain.spring.hibernate.jta.SpringJtaPlatformAdapter">
<!-- the mainTransactionManager is defined in ora_jtam_atomikos.xml imported -->
<property name="jtaTransactionManager" ref="mainTransactionManager" />
</bean>
<bean id="entityManagerFactoryEVL"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
depends-on="mainTransactionManager,springJtaPlatformAdapter">
<property name="persistenceXmlLocation" value="classpath:evl_persistence.xml" />
<property name="persistenceUnitName" value="evlPersistenceUnit" />
<property name="dataSource" ref="optionalDataSource" />
<property name="loadTimeWeaver">
<bean
class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>
<property name="jpaPropertyMap" ref="jpaPropertyMapEVL"></property>
</bean>
<util:map id="jpaPropertyMapEVL">
<entry key="hibernate.hbm2ddl.auto" value="validate" />
<entry key="hibernate.show_sql" value="false" />
<entry key="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect" />
<entry key="hibernate.transaction.jta.platform"
value="my.domain.spring.hibernate.jta.SpringJtaPlatformAdapter" />
</util:map>
<bean id="entityManagerFactoryVVL"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
depends-on="mainTransactionManager,springJtaPlatformAdapter">
<property name="persistenceXmlLocation" value="classpath:vvl_persistence.xml" />
<property name="persistenceUnitName" value="vvlPersistenceUnit" />
<property name="dataSource" ref="eventDataSource" />
<property name="loadTimeWeaver">
<bean
class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>
<property name="jpaPropertyMap" ref="jpaPropertyMapVVL"></property>
</bean>
<util:map id="jpaPropertyMapVVL">
<entry key="hibernate.hbm2ddl.auto" value="validate" />
<entry key="hibernate.show_sql" value="false" />
<entry key="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect" />
<entry key="hibernate.transaction.jta.platform"
value="my.domain.spring.hibernate.jta.SpringJtaPlatformAdapter" />
</util:map>
And the small class named SpringJtaPlatformAdapter :
public class SpringJtaPlatformAdapter extends AbstractJtaPlatform {
private static final long serialVersionUID = -7030175748923257913L;
private static TransactionManager sTransactionManager;
private static UserTransaction sUserTransaction;
@Override
protected TransactionManager locateTransactionManager() {
Assert.notNull(sTransactionManager, "TransactionManager has not been setted");
return sTransactionManager;
}
@Override
protected UserTransaction locateUserTransaction() {
Assert.notNull(sUserTransaction, "UserTransaction has not been setted");
return sUserTransaction;
}
public void setJtaTransactionManager(JtaTransactionManager jtaTransactionManager) {
sTransactionManager = jtaTransactionManager.getTransactionManager();
sUserTransaction = jtaTransactionManager.getUserTransaction();
}
}
When I do run the batch, I could verified that :
- the
atomikosUserTransaction
andatomikosTransactionManager
of Atomikos are constructed first - the
mainTransactionManager
is initialized right after - the
setJtaTransactionManager
method of mySpringJtaPlatformAdapter
is called, both memory addresses for thesTransactionManager
andsUserTransaction
are consistent with the ones created before - the
locateTransactionManager
of theSpringJtaPlatformAdapter
is called twice (one for each persistence unit) - my Hibernate code is then performed, my entities are correctly initialized
- the database which is accessed through JDBC is updated
- the databases which are accessed through Hibernate are NOT updated (as if a rollback took place)
During the run, only one warning appears in the logs :
WARN main SessionFactoryImpl:1530 - HHH000008: JTASessionContext being used with JDBCTransactionFactory; auto-flush will not operate correctly with getCurrentSession()
Maybe that can help, I personnaly don't get the warning message.
According to Maven, I'm using Spring ORM 3.2.0 with Hibernate 4.2.3 and Atomikos 3.8.0.