【问题标题】:Configure an Atomikos UserTransactionManager for Hibernate in Spring Batch在 Spring Batch 中为 Hibernate 配置 Atomikos UserTransactionManager
【发布时间】: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();
  }

}

当我运行批处理时,我可以验证:

  1. atomikosUserTransactionatomikosTransactionManager 的 首先构建 Atomikos
  2. mainTransactionManager 随即被初始化
  3. 我的SpringJtaPlatformAdaptersetJtaTransactionManager方法被调用,sTransactionManagersUserTransaction的内存地址和之前创建的一致
  4. SpringJtaPlatformAdapterlocateTransactionManager 被调用两次(每个持久性单元调用一次)
  5. 然后执行我的 Hibernate 代码,我的实体已正确初始化
  6. 更新通过 JDBC 访问的数据库
  7. 通过 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


    【解决方案1】:

    我正在使用 Atomikos 4.0.0.M4 版本:

    <entry key="hibernate.transaction.jta.platform"
      value="com.atomikos.icatch.jta.hibernate4.AtomikosPlatform"/>
    

    代替:

    <entry key="hibernate.transaction.manager_lookup_class"
      value="com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup" />
    

    需要注意的是,Hibernate 4.x moved away from TransactionManager to JtaPlatform 需要更改配置。

    我对 Atomikos 提供的功能感到满意,并且它对我来说运行稳定。

    【讨论】:

    • 它确实解决了异常。现在我的批次正在完成。但是 Hibernate 数据库更改不会持久化。就好像它在提交 JDBC 查询时对 Hibernate 进行了回滚......
    • 仅从配置很难预测。您必须调试您的应用程序以找出阻止 Hibernate 持久保存到数据库的原因。
    • 我很确定我配置的 UserTransaction 不会被 Hibernate 访问。相反,我怀疑TransactionManagerLookup 确实创建了一个新的UserTransaction,它由两个Hibernate 实体管理器共享。 spring 提交是在id="atomikosUserTransaction" 标识的事务上执行的,因此不为 Hibernate 执行。我将尝试深入调试库以查看两个事务的内存地址。但这可能令人不安......
    • 通过调试验证只创建了一个UserTransactionManager。我更新了我的问题以反映我对配置所做的最新更改。
    【解决方案2】:

    一位同事发现了为什么我的休眠数据库没有更新。我在我的 persistence.xml 中有这个:

    <persistence-unit name="evlPersistenceUnit" transaction-type="RESOUCE_LOCAL">
    

    对于 Atomikos,我应该放置:

    <persistence-unit name="evlPersistenceUnit" transaction-type="JTA">
    

    现在它工作得很好。

    【讨论】:

      猜你喜欢
      • 2016-11-14
      • 2017-06-24
      • 2015-07-07
      • 1970-01-01
      • 2015-12-21
      • 2021-10-06
      • 2021-09-08
      • 2018-01-17
      • 1970-01-01
      相关资源
      最近更新 更多