【发布时间】:2015-04-30 04:20:37
【问题描述】:
我需要做的是在三个不同的 Oracle 数据库上进行分布式事务。其中一个必须通过 JDBC 访问,另外两个必须通过 Hibernate 访问。这是我的 Atomikos 配置:
<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" />
Hibernate 配置的灵感来自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>
还有名为 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();
}
}
当我运行批处理时,我可以验证:
-
atomikosUserTransaction和atomikosTransactionManager的 首先构建 Atomikos -
mainTransactionManager随即被初始化 - 我的
SpringJtaPlatformAdapter的setJtaTransactionManager方法被调用,sTransactionManager和sUserTransaction的内存地址和之前创建的一致 -
SpringJtaPlatformAdapter的locateTransactionManager被调用两次(每个持久性单元调用一次) - 然后执行我的 Hibernate 代码,我的实体已正确初始化
- 更新通过 JDBC 访问的数据库
- 通过 Hibernate 访问的数据库没有更新(就像发生了回滚一样)
在运行过程中,日志中只出现一条警告:
WARN main SessionFactoryImpl:1530 - HHH000008: JTASessionContext being used with JDBCTransactionFactory; auto-flush will not operate correctly with getCurrentSession()
也许这会有所帮助,我个人没有收到警告信息。
根据 Maven,我将 Spring ORM 3.2.0 与 Hibernate 4.2.3 和 Atomikos 3.8.0 一起使用。
【问题讨论】:
标签: spring hibernate jpa spring-batch atomikos