繁体   English   中英

跨越两个 Hibernate 事务管理器的一种服务方法上的 Spring @Transactional

[英]Spring @Transactional on one service method spanning over two Hibernate transaction managers

我想知道是否可以在一种服务方法中使用两个事务管理器。 因为由于客户端mysql db配置的限制,我们在一个数据库中有2个不同的数据源,即每个模式一个user/pwd/url。 这就是为什么我必须配置两个事务管理器。 现在,当涉及到服务实现时,我遇到了问题。 请参阅以下代码:

public class DemoService{
    ...
    @Transactional(value = "t1")
    public doOne(){
        doTwo();
    }

    @Transactional(value = "t2")
    public doTwo(){

    }
    ...
}

如果我使用此代码模式,我总是会遇到异常

org.hibernate.HibernateException: No Session found for current thread

如果我分别运行这两种方法,它工作正常。 我错过了什么? 或者这里还有其他工作? 任何意见,将不胜感激。

顺便说一句:我的一些配置

    <bean id="sessionFactorySso" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="mappingLocations">
        <list>
            <value>classpath*:sso.vo/*.hbm.xml</value>
        </list>
    </property>

    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="generateDdl">true</prop>
            <prop key="hibernate.dialect">${dialect} </prop>
        </props>
    </property>
    <property name="dataSource" ref="dataSourceSso"/>
</bean>

<bean id="dataSourceSso" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
    <property name="driverClass" value="${driver}"/>
    <property name="jdbcUrl" value="${sso.url}"/>
    <property name="user" value="${sso.username}"/>
    <property name="password" value="${sso.password}"/>
         <!-- these are C3P0 properties -->
    <property name="acquireIncrement" value="2" />
    <property name="minPoolSize" value="1" />
    <property name="maxPoolSize" value="2" />
    <property name="automaticTestTable" value="test_c3p0" />
    <property name="idleConnectionTestPeriod" value="300" />
    <property name="testConnectionOnCheckin" value="true" />
    <property name="testConnectionOnCheckout" value="true" />
    <property name="autoCommitOnClose" value="true" />
    <property name="checkoutTimeout" value="1000" />
    <property name="breakAfterAcquireFailure" value="false" />
    <property name="maxIdleTime" value="0" />
</bean>

<bean id="transactionManagerSso" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactorySso"/>
    <qualifier value="sso" />
</bean>

<tx:annotation-driven transaction-manager="transactionManagerSso" />

因为您想在一个事务中登记两个数据源,所以您需要 XA(Global) Transaction。

因此,您需要:

  1. 设置 Spring JTA 事务管理器
  2. 您的 Hibernate 属性应该使用 JTA 平台设置
  3. 您的数据源连接应该是 XA 投诉
  4. 您需要一个应用服务器 JTA 事务管理器或一个独立的 tarnsaction 管理器(Bitronix、Atomikos、JOTM)
  5. 您将需要两个会话工厂配置,一个用于每个单独的数据源。
  6. 而且您不会有两个事务管理器:t1 和 t2,但是您将征用两个事务性 XA 数据源,它们将自动征入同一个全局事务中,这意味着您将有两个 XA 连接征入同一个全局事务中。 XA 事务将使用 2PC 协议在提交时间提交这两个资源。

查看这个Bitronix Hibernate 示例

你有几个选择:

  1. 将 bean 注入自身并使用引用调用 doTwo()。 这确实违背了 IoC 和 AOP 的整体理念,所以我不推荐它。
  2. 切换到编译时编织。 Spring(实际上是 AspectJ 编译器)将在编译时将字节码添加到您的类中,而不是使用代理。 这种方法有利有弊。 有关详细信息,请参阅此页面
  3. 使用加载时间编织。 与 #2 相同,只是您的类在加载时而不是在编译时进行修改。 IMO,Java 类加载已经够复杂了。 我确信这对某些人来说非常有用,但我个人会避免这种情况。
  4. 正如 Vlad 指出的那样,您可以使用 JTA 和 XA。
  5. 在调用 doTwo() 之前,在 doOne() 中针对事务管理器 2 启动一个新事务。 RTFM 关于程序化事务管理
  6. 查看ChainedTransactionManager 它本质上聚合了多个事务管理器,并在提交/回滚方面做出了“最大努力”。 这不是像 Vlad 的解决方案那样真正的两阶段提交。

除了 Vlad 的解决方案(#4)之外,所有这些都有可能使数据库处于不一致的状态。 您需要使用 JTA/XA/两阶段提交来确保在其中一个 TX 管理器在提交时引发异常的情况下的一致性。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM