Spring Hibernate @transactional一个服务方法有两个transactionManager吗?

时间:2014-05-20 10:15:04

标签: java spring hibernate java-ee

我想知道是否可以在一种服务方法中使用两个事务管理器。 因为客户端的mysql数据库配置有限,我们在一个数据库中有两个不同的数据源,即每个模式一个用户/ 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

如果我单独运行这两种方法,它工作正常。 我错过了什么?或者还有其他工作吗? 任何建议将不胜感激。

btw:我的一些配置

    <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" />

2 个答案:

答案 0 :(得分:0)

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

因此你需要:

  1. 设置Spring JTA事务管理器
  2. 您的Hibernate属性应使用JTA平台设置
  3. 您的数据源连接应为XA投诉
  4. 您需要一个应用服务器JTA事务管理器或一个独立的tarnsaction管理器(Bitronix,Atomikos,JOTM)
  5. 您将需要两个会话工厂配置,每个配置对应一个数据源。
  6. 您将不会有两个事务管理器:t1和t2,而是您将登记两个事务性XA数据源,这些数据源将自动登记在同一个全局事务中,这意味着您将在同一个全局事务中登记两个XA连接全球交易。 XA事务将使用2PC协议在提交时提交两个资源。
  7. 结帐Bitronix Hibernate example

答案 1 :(得分:0)

您有几个选择:

  1. 将bean注入其自身并使用该引用调用doTwo()。这实际上违背了IoC和AOP的整个想法,因此我不推荐它。
  2. 切换到编译时编织。而不是使用代理,Spring(实际上是AspectJ编译器)将在编译时添加字节码以启动/停止事务到您的类。这种方法有利有弊。有关详细信息,请参阅this page
  3. 使用加载时间编织。与#2相同,除了您的类在加载时而不是在编译时被修改。 IMO,Java类加载足够复杂。我相信这对一些人来说很有用,但我个人会避免这种情况。
  4. 正如Vlad指出的那样,你可以使用JTA和XA。
  5. 在调用doTwo()之前,在doOne()内针对事务管理器2启动一个新事务。 RTFM programmatic transaction management
  6. 查看ChainedTransactionManager。它本质上聚合了多个事务管理器,并通过提交/回滚进行“尽力而为”。这不像Vlad的解决方案那样真正的两阶段提交。
  7. 除了Vlad的解决方案(#4)之外,所有这些都有可能使数据库处于不一致状态。您需要使用JTA / XA /两阶段提交,以确保其中一个TX管理器在提交时抛出异常时保持一致性。