2个本地事务(在不同的数据源上)相互之间

时间:2014-08-21 10:49:17

标签: java spring spring-transactions

我遇到了交易问题。我有一个方法需要从一个数据源读取数据,将其写入另一个数据源,然后提交到已读取数据的第一个数据源。 (第一个数据源中的数据在队列中,我需要提交将它们从队列中删除)。

如果我们进行全球JTA交易,我不会担心这一点,但我们不会这样做。由于架构原因,我们只能使用本地交易。 (我不是建筑师)

这是我做的(简化):

@Transactional(value="tx1", propagation=REQUIRES_NEW)
public void copy() {
  try {
    Data data = read();
    service.write(data);
  } catch (Exception x) {...}
}

Service.java:

@Transactional(value="tx2", propagation=REQUIRES_NEW)
public void write(Data data) {
  ...
}

我知道当队列中的提交失败时我遇到了问题,因为我已经编写了数据。但这不是问题。而不是这个问题的一部分。

问题是,我收到以下错误:

2014.08.21 12:29:54 INFO : 21.08.2014 12:29:54 org.springframework.transaction.IllegalTransactionStateException: Pre-bound JDBC Connection found! JpaTransactionManager does not support running within DataSourceTransactionManager if told to manage the DataSource itself. It is recommended to use a single JpaTransactionManager for all transactions on a single DataSource, no matter whether JPA or JDBC access.
    at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:311)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:371)
    at sun.reflect.GeneratedMethodAccessor50.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309)
    at org.springframework.osgi.service.importer.support.internal.aop.ServiceInvoker.doInvoke(ServiceInvoker.java:58)
    at org.springframework.osgi.service.importer.support.internal.aop.ServiceInvoker.invoke(ServiceInvoker.java:62)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:131)
    at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionIntercept [StdOut]
2014.08.21 12:29:54 INFO : or.java:119)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.osgi.service.util.internal.aop.ServiceTCCLInterceptor.invokeUnprivileged(ServiceTCCLInterceptor.java:56)
    at org.springframework.osgi.service.util.internal.aop.ServiceTCCLInterceptor.invoke(ServiceTCCLInterceptor.java:39)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.osgi.service.importer.support.LocalBundleContextAdvice.invoke(LocalBundleContextAdvice.java:59)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:131)
    at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:119)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    at com.sun.proxy.$Proxy797.getTransaction(Unknown Source)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:335)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:105)
    at org.springframewo [StdOut]
2014.08.21 12:29:54 INFO : rk.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    at com.sun.proxy.$Proxy655.write(Unknown Source)
我正在做一些不应该做的事情吗?任何人都对我应该做的不同有很好的建议吗?

1 个答案:

答案 0 :(得分:1)

这只是线索(没有经过测试),但是太长了,不适合评论。

首先应确保错误确实是由2个@Transactional注释方法的级联引起的,首先使它们独立(请参阅我的上述注释)。对于帖子的后续内容,我认为这是真的。

您可以尝试使用明确的程序化事务管理,希望您从Spring中获得的魔法越少,您获得的副作用就越小。

// Beans to be injected
PlatformTransactionManager tx1;
PlatformTransactionManager tx2;
TransactionDefinition td1 = new ;
TransactionDefinition td2;

SessionFactory sf1;
SessionFactory sf2;

// method using explicit transactions
public void copy(Serializable id) throws Exception {
    TransactionStatus status1 = tx1.getTransaction(td1);
    try {
        Session session1 = sf1.getCurrentSession();
        Object data = session1.get("Entity", id);
        TransactionStatus status2 = tx2.getTransaction(td1);
        try {
            Session session2 = sf2.getCurrentSession();
            session2.saveOrUpdate(data);
        }
        catch (Exception e2) {
            tx2.rollback(status2);
            throw e2;
        }
        tx2.commit(status2);
    }
    catch (Exception e1) {
        tx1.rollback(status1);
        throw e1;
    }
    tx1.commit(status1);
}

但由于HibernateTransactionManager中的这句话,javadoc: JTA(通常通过JtaTransactionManager)是访问同一事务中多个事务资源所必需的。 Hibernate使用的DataSource需要在这种情况下启用JTA(参见容器设置),我不确定它是否可以在JTA之外工作。

另一种解决方案:根据这两篇帖子Distributed transactions in Spring, with and without XAImplementing Spring ChainedTransactionManager according to the “best efforts 1PC” pattern,您还可以使用spring-data-common中的ChainedTransactionManager

从第一篇文章中摘录:

ChainedTransactionManager的配置

<bean id="transactionManager" class="com.springsource.open.db.ChainedTransactionManager">
  <property name="transactionManagers">
    <list>
      <bean
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
      </bean>
      <bean
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="otherDataSource" />
      </bean>
    </list>
  </property>
</bean>

...请记住,资源的顺序很重要。它们是嵌套的,并且提交或回滚的顺序与它们被登记的顺序相反(这是配置中的顺序)

但我仍然不确定它是否可以在JTA之外工作......

如果这些线索都没有成功,您将不得不再次询问您的系统架构师JTA是否不是一个选项。