0

I am learning JTA and I have two databases one from oracle and another one from mysql. I have noticed a weird behavior I hope someone can shed some light please, the code and spring config are shown below.

Please find use case below:

  1. When two save operations (userRepository.save,tutorialsRepository.save) from different database are called from the method (checkDistributedTransactions) and a runtime exception is thrown at the end purposely of the method then both table operation rolls back which is as expected for JTA.

  2. When a find method(findByActive) is added in between the two save methods and a runtime exception is thrown at the end, only the last save method(tutorialsRepository.save) rolls back and the first one does not roll back. The issue is that I would expect both save method to roll back or i am wrong?

  3. Finally when the find method is added before the two save method and a runtime exception is thrown at the end then both save method roll back as expected.

Please find implementation class below:

@Component
public class UserServiceImp {

    @Autowired
    UserRepository userRepository;

    UserTable user;
    @Autowired
    TutorialsRepository tutorialsRepository;

    Tutor tutorials;

    String name;

    @Transactional()
    public void checkDistributedTransactions() {


        user = new UserTable();
        user.setUsername("testFoo");
        user.setFirstname("firstname");
        user.setLastname("lastname");
        user.setActive("Y");

        userRepository.save(user);

         List<UserTable> userTableList = userRepository.findByActive("Y");

        System.out.println("userTableList " + userTableList.size());


        tutorials = new Tutor();
        tutorials.setTutorial_id("3");
        tutorials.setTutorial_title("hrm");

        tutorialsRepository.save(tutorials);


         throw new EmptyStackException();

    }

}

Please find app context below:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xmlns:transaction="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.8.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
    <context:component-scan base-package="org.example" />
    <bean
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
        id="catalogEntityManagerFactory">
        <property name="jpaProperties">
            <map>

                <entry key="hibernate.transaction.jta.platform" value-ref="jtaPlatform" />

            </map>
        </property>
        <property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
        <property name="jtaDataSource">
            <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
                <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />

                <property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
                <property name="username" value="hr" />
                <property name="password" value="unknown" />


            </bean>
        </property>
        <property name="packagesToScan" value="org.example.domain.catalog" />
    </bean>


    <bean
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
        id="directoryEntityManagerFactory">
        <property name="jpaProperties">
            <map>
                <entry key="hibernate.transaction.jta.platform" value-ref="jtaPlatform" />
            </map>
        </property>
        <property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
        <property name="jtaDataSource">
            <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
                <property name="driverClassName" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://localhost/mysqlDb" />
                <property name="username" value="root" />
                <property name="password" value="unknown" />
            </bean>
        </property>
        <property name="packagesToScan" value="org.example.domain.directory" />
    </bean>


    <jpa:repositories base-package="org.example.domain.directory"
        entity-manager-factory-ref="directoryEntityManagerFactory" />

    <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
        id="jpaVendorAdapter">
        <property name="databasePlatform" value="org.hibernate.dialect.H2Dialect" />
        <property name="showSql" value="true" />
    </bean>
    <context:annotation-config />

    <jpa:repositories base-package="org.example.domain.catalog"
        entity-manager-factory-ref="catalogEntityManagerFactory" />


    <transaction:annotation-driven />
    <bean class="java.lang.String" id="jtaPlatform">
        <constructor-arg value="com.atomikos.icatch.jta.hibernate4.AtomikosPlatform" />
    </bean>

    <bean class="org.springframework.transaction.jta.JtaTransactionManager"
        id="transactionManager">
        <property name="transactionManager">
            <bean class="com.atomikos.icatch.jta.UserTransactionManager"
                init-method="init" destroy-method="close">
                <property name="forceShutdown" value="false" />
            </bean>
        </property>
        <property name="userTransaction">
            <bean class="com.atomikos.icatch.jta.J2eeUserTransaction">
                <property name="transactionTimeout" value="300" />
            </bean>
        </property>
        <property name="allowCustomIsolationLevels" value="true" />
    </bean>


</beans>

The method to call the implementation class below:

public static void main(String[] args) {

        ApplicationContext context = new ClassPathXmlApplicationContext(new String[] { "springContext.xml" });

        UserServiceImp userServiceImp = (UserServiceImp) context.getBean("userServiceImp");

        userServiceImp.checkDistributedTransactions();

    }

Any idea when the find operation is added in between the two save method then why only the last save method rolls back instead of both?

Thanks in advance

user1999453
  • 1,297
  • 4
  • 29
  • 65
  • It sounds like the find operation is causing a Hibernate [auto flush](https://docs.jboss.org/hibernate/orm/5.1/userguide/html_single/chapters/flushing/Flushing.html). Typically the save() is not flushed to the database until the final commit, but the overlapping find needs the new data to be to be in the database first. That makes it sound like a transaction is not being started, or auto-commit is enabled, to prevent the rollback, though I can't spot the cause there. – df778899 Mar 04 '18 at 15:08
  • thanks i am still trying to figure out the issue – user1999453 Mar 05 '18 at 09:48
  • The answer: https://stackoverflow.com/questions/21672454/application-managed-jpa-when-is-transaction-needed – user1999453 Mar 05 '18 at 11:59

0 Answers0