1

I'm facing a problem with transactions. I have a method that needs to read data from one datasource, write it to another and then commit to the first datasource that the data have been read. (the data in the first datasource is in a queue and I need to commit to remove them from the queue).

I wouldn't worry about this if we had global JTA transactions, but we don't. Due to architectural reasons we can only use local transactions. (I'm not the architect)

Here is what I did (simplified):

@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) {
  ...
}

I know that I have a problem when the commit on the queue fails because I have already written the data. But that is not a problem. And not part of this question.

The problem is, I get following error:

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)

am I doing something that should not be done? Anyone have a good suggestion on what I should do differently?

EasterBunnyBugSmasher
  • 1,507
  • 2
  • 15
  • 34
  • Never tried that, but I think I would try to avoid cascading transactions even on different datasources. Something like `[outside transactions] Service.copy() { data = service1.read(); service2.write(data); }` `@Transactional("t1") public class Service1` `@Transactional("t2") public class Service2` – Serge Ballesta Aug 21 '14 at 14:37
  • but that way, I have service1.read() commited before I wrote the data to service2 – EasterBunnyBugSmasher Aug 21 '14 at 15:38
  • You should try, at least to be sure whether the problem is the transactions cascade or not. – Serge Ballesta Aug 21 '14 at 16:42
  • Can you please mention spring transactional configuration? – Prakash Bhagat Aug 22 '14 at 11:13

1 Answers1

1

This is only clues (nothing tested), but is much too long to fit in a comment.

You should first make sure that the error is really caused by the cascading of 2 @Transactional annotated methods by first making them independant (see my above comment). For the following of the post, I assume it is true.

You could try to use explicit programmatic transaction management, hoping that the less magic you ask form Spring, the less side effects you will get.

// 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);
}

But because of this sentence in HibernateTransactionManager javadoc : JTA (usually through JtaTransactionManager) is necessary for accessing multiple transactional resources within the same transaction. The DataSource that Hibernate uses needs to be JTA-enabled in such a scenario (see container setup), I am not sure whether it can works outside JTA.

Another solution : According to those 2 posts Distributed transactions in Spring, with and without XA and Implementing Spring ChainedTransactionManager according to the “best efforts 1PC” pattern, you could also use the ChainedTransactionManager from spring-data-common.

Extract from first post :

Configuration of the 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>

... Remember that the order of the resources is significant. They are nested, and the commit or rollback happens in reverse order to the order they are enlisted (which is the order in the configuration)

But I am still unsure whether it can work outside JTA ...

And if none of this clues is successful, you will have to ask again your system architect whether JTA could not be an option.

Community
  • 1
  • 1
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252