6.8.3.5 Configure JPA-Based Spring REST Apps with XA-Compliant Resource Manager

Use the information provided in this section to configure Hibernate or EclipseLink as the JPA provider for your Helidon or Spring REST applications which participates in an XA transaction when you use an XA-compliant resource manager.

Your application can connect to multiple XA-compliant resource managers. If you are using multiple XA-compliant resource managers for your application, complete the following steps for each resource manager.
  1. Configure property values for the MicroTx client library.

    The following example provides sample values for the properties. Provide the values based on your environment.

    spring:
      microtx:
        participant-url: https://bookTicket-app:8081
        propagation-active: true
        http-client-connection-pool-size: 15
        xa-transaction-timeout: 60000
        xa-resource-manager-id: 174A5FF2-D8B1-47B0-AF09-DA5AFECA2F61
        xa-xa-support: true
    

    Ensure that xa-xa-support is set to true.

    For details about each property and other optional properties, see Configure Library Properties for Spring REST Apps.

  2. Include only one of the following MicroTx library files as a maven dependency in the Spring Boot 3.x application's pom.xml file. The following sample code is for the 24.2 release. Provide the correct version, based on the release version that you want to use. Spring Boot 3.x applications work with Java 17.
    • For JDBC applications, use the microtx-spring-boot-starter file.

      <dependency>
           <groupId>com.oracle.microtx</groupId>
           <artifactId>microtx-spring-boot-starter</artifactId>
           <version>24.2</version>
      </dependency>
    • For Java Apps that use Hibernate as JPA provider, use the microtx-spring-boot-starter-hibernate library file.

      <dependency>
           <groupId>com.oracle.microtx</groupId>
           <artifactId>microtx-spring-boot-starter-hibernate</artifactId>
           <version>24.2</version>
      </dependency>
    • For Java Apps that use EclipseLink as JPA provider, use the microtx-spring-boot-starter-eclipselink library file.

      <dependency>
           <groupId>com.oracle.microtx</groupId>
           <artifactId>microtx-spring-boot-starter-eclipselink</artifactId>
           <version>24.2</version>
      </dependency>
  3. Create a .java file in the folder that contains your application code to initialize an XADataSourceConfig object. The XADataSourceConfig class contains methods to create custom data source and entity manager factory objects.

    The custom data source object contains details to connect with the resource manager. It is the responsibility of the application developer to ensure that an XA-compliant JDBC driver and required parameters are set up while creating a custom data source object.

    • The following example code for Hibernate apps using Spring REST shows how you can initialize the library in within the XADataSourceConfig class, create a custom data source named departmentDataSource, and create an entity manager factory object named emf. You can create a similar code for your application.

      Ensure that you specify com.oracle.microtx.jpa.HibernateXADataSourceConnectionProvider as the property value for hibernate.connection.provider_class.

      package com.oracle.mtm.sample;
      
      import com.oracle.microtx.common.MicroTxConfig;
      import oracle.ucp.jdbc.PoolDataSourceFactory;
      import oracle.ucp.jdbc.PoolXADataSource;
      import org.hibernate.jpa.HibernatePersistenceProvider;
      import org.springframework.beans.factory.annotation.Value;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.ComponentScan;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.annotation.Primary;
      import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
      import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
      
      import javax.persistence.EntityManagerFactory;
      import javax.sql.DataSource;
      import javax.sql.XADataSource;
      import java.sql.SQLException;
      import java.util.Properties;
      
      @Configuration
      @ComponentScan("com.oracle")
      public class XADataSourceConfig {
          @Value("${departmentDataSource.url}")
          private String url;
          @Value("${departmentDataSource.user}")
          private String username;
          @Value("${departmentDataSource.password}")
          private String password;
          @Value("${departmentDataSource.oracleucp.min-pool-size}")
          private String minPoolSize;
          @Value("${departmentDataSource.oracleucp.initial-pool-size:10}")
          private String initialPoolSize;
      
          @Value("${departmentDataSource.oracleucp.max-pool-size}")
          private String maxPoolSize;
      
          @Value("${departmentDataSource.oracleucp.data-source-name}")
          private String dataSourceName;
      
          @Value("${departmentDataSource.oracleucp.connection-pool-name}")
          private String connectionPoolName;
      
          @Value("${departmentDataSource.oracleucp.connection-factory-class-name:oracle.jdbc.xa.client.OracleXADataSource}")
          private String connectionFactoryClassName;
      
          @Value("${spring.microtx.xa-resource-manager-id}")
          private String resourceManagerId;
      
          @Bean(name = "ucpXADataSource")
          @Primary
          public DataSource getDataSource() {
              DataSource pds = null;
              try {
                  pds = PoolDataSourceFactory.getPoolXADataSource();
      
                  ((PoolXADataSource) pds).setConnectionFactoryClassName(connectionFactoryClassName);
                  ((PoolXADataSource) pds).setURL(url);
                  ((PoolXADataSource) pds).setUser(username);
                  ((PoolXADataSource) pds).setPassword(password);
                  ((PoolXADataSource) pds).setMinPoolSize(Integer.valueOf(minPoolSize));
                  ((PoolXADataSource) pds).setInitialPoolSize(Integer.valueOf(initialPoolSize));
                  ((PoolXADataSource) pds).setMaxPoolSize(Integer.valueOf(maxPoolSize));
      
                  ((PoolXADataSource) pds).setDataSourceName(dataSourceName);
                  ((PoolXADataSource) pds).setConnectionPoolName(connectionPoolName);
      
                  System.out.println("XADataSourceConfig: XADataSource created");
              } catch (SQLException ex) {
                  System.err.println("Error connecting to the database: " + ex.getMessage());
              }
              return pds;
          }
      
      
          @Bean(name = "entityManagerFactory")
          public EntityManagerFactory createEntityManagerFactory() throws SQLException {
              LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
      
              entityManagerFactoryBean.setDataSource(getDataSource());
              entityManagerFactoryBean.setPackagesToScan(new String[] { "com.oracle.mtm.sample.entity" });
              entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
      
              entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
              entityManagerFactoryBean.setPersistenceUnitName("mydeptxads");
              Properties properties = new Properties();
              properties.setProperty( "javax.persistence.transactionType", "RESOURCE_LOCAL"); // change this to resource_local
              properties.put("hibernate.show_sql", "true");
              properties.put("hibernate.dialect", "org.hibernate.dialect.Oracle12cDialect");
              properties.put("hibernate.format_sql", "true");
              properties.put("hbm2ddl.auto", "validate");
              properties.put("hibernate.connection.provider_class", "com.oracle.microtx.jpa.HibernateXADataSourceConnectionProvider");
              entityManagerFactoryBean.setJpaProperties(properties);
              entityManagerFactoryBean.afterPropertiesSet();
              EntityManagerFactory emf = (EntityManagerFactory) entityManagerFactoryBean.getObject();
              System.out.println("entityManagerFactory = " + emf);
              MicroTxConfig.initEntityManagerFactory(emf, resourceManagerId); // Initialize TMM Library
              return emf;
          }
      }
    • The following example code for EcpliseLink apps using Spring REST shows how you can initialize the library in within the PoolXADataSource class, create a custom data source named departmentDataSource, and create an entity manager factory object named emf. You can create a similar code for your application.

      Ensure that you specify values for the PersistenceUnitProperties.JDBC_CONNECTOR and PersistenceUnitProperties.SESSION_EVENT_LISTENER_CLASS properties exactly as mentioned in the following sample code.

      package com.oracle.mtm.sample;
      
      import com.oracle.microtx.common.MicroTxConfig;
      import oracle.ucp.jdbc.PoolDataSourceFactory;
      import oracle.ucp.jdbc.PoolXADataSource;
      import org.eclipse.persistence.config.PersistenceUnitProperties;
      import org.springframework.beans.factory.annotation.Value;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.ComponentScan;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.annotation.Primary;
      import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
      import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter;
      import org.eclipse.persistence.jpa.PersistenceProvider;
      
      import javax.persistence.EntityManagerFactory;
      import javax.sql.DataSource;
      import javax.sql.XADataSource;
      import java.sql.SQLException;
      import java.util.Properties;
      
      @Configuration
      @ComponentScan("com.oracle.microtx")
      public class XADataSourceConfig {
          @Value("${departmentDataSource.url}")
          private String url;
          @Value("${departmentDataSource.user}")
          private String username;
          @Value("${departmentDataSource.password}")
          private String password;
      
          @Value("${departmentDataSource.oracleucp.min-pool-size}")
          private String minPoolSize;
          @Value("${departmentDataSource.oracleucp.initial-pool-size:10}")
          private String initialPoolSize;
      
          @Value("${departmentDataSource.oracleucp.max-pool-size}")
          private String maxPoolSize;
      
          @Value("${departmentDataSource.oracleucp.data-source-name}")
          private String dataSourceName;
      
          @Value("${departmentDataSource.oracleucp.connection-pool-name}")
          private String connectionPoolName;
      
          @Value("${departmentDataSource.oracleucp.connection-factory-class-name:oracle.jdbc.xa.client.OracleXADataSource}")
          private String connectionFactoryClassName;
      
          @Value("${spring.microtx.xa-resource-manager-id}")
          private String resourceManagerId;
      
         @Bean(name = "ucpXADataSource")
          @Primary
          public DataSource getDataSource() {
              DataSource pds = null;
              try {
                  pds = PoolDataSourceFactory.getPoolXADataSource();
      
                  ((PoolXADataSource) pds).setConnectionFactoryClassName(connectionFactoryClassName);
                  ((PoolXADataSource) pds).setURL(url);
                  ((PoolXADataSource) pds).setUser(username);
                  ((PoolXADataSource) pds).setPassword(password);
                  ((PoolXADataSource) pds).setMinPoolSize(Integer.valueOf(minPoolSize));
                  ((PoolXADataSource) pds).setInitialPoolSize(Integer.valueOf(initialPoolSize));
                  ((PoolXADataSource) pds).setMaxPoolSize(Integer.valueOf(maxPoolSize));
      
                  ((PoolXADataSource) pds).setDataSourceName(dataSourceName);
                  ((PoolXADataSource) pds).setConnectionPoolName(connectionPoolName);
      
      
                  System.out.println("XADataSourceConfig: XADataSource created");
              } catch (SQLException ex) {
                  System.err.println("Error connecting to the database: " + ex.getMessage());
              }
              return pds;
          }
      
          @Bean(name = "entityManagerFactory")
          public EntityManagerFactory createEntityManagerFactory() throws SQLException {
              LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
      
              entityManagerFactoryBean.setDataSource(getDataSource());
              entityManagerFactoryBean.setPackagesToScan(new String[] { "com.oracle.mtm.sample.entity" });
              entityManagerFactoryBean.setJpaVendorAdapter(new EclipseLinkJpaVendorAdapter());
              entityManagerFactoryBean.setPersistenceProviderClass(PersistenceProvider.class);
              entityManagerFactoryBean.setPersistenceUnitName("mydeptxads");
      
              Properties properties = new Properties();
              properties.setProperty( "javax.persistence.transactionType", "RESOURCE_LOCAL"); // change this to resource_local
              properties.setProperty("javax.persistence.jdbc.driver", "oracle.jdbc.OracleDriver");
              properties.setProperty("javax.persistence.jdbc.url", url);
              properties.setProperty("javax.persistence.jdbc.user", username);
              properties.setProperty("javax.persistence.jdbc.password", password);
      
              properties.setProperty(PersistenceUnitProperties.CACHE_SHARED_DEFAULT, "false");
              properties.setProperty(PersistenceUnitProperties.TARGET_DATABASE, "Oracle");
              properties.setProperty(PersistenceUnitProperties.WEAVING, "false");
              properties.setProperty(PersistenceUnitProperties.JDBC_CONNECTOR, "com.oracle.microtx.eclipselink.EclipseLinkXADataSourceConnector");
              properties.setProperty(PersistenceUnitProperties.SESSION_EVENT_LISTENER_CLASS, "com.oracle.microtx.eclipselink.EclipseLinkXASessionEventAdaptor");
      
              entityManagerFactoryBean.setJpaProperties(properties);
              entityManagerFactoryBean.afterPropertiesSet();
              EntityManagerFactory emf = (EntityManagerFactory) entityManagerFactoryBean.getObject();
              System.out.println("entityManagerFactory = " + emf);
              MicroTxConfig.initEntityManagerFactory(emf, resourceManagerId); // Initialize TMM Library
              return emf;
          }
      }
  4. To initialize the Entity Manager Factory object, pass the required parameters to MicroTxConfig.initEntityManagerFactory() based on whether your application connects to a single resource manager or multiple resource managers.
    • When your application connects to a single resource manager, create an entity manager factory object and then pass it to the MicroTx library. In the following sample code, emf is the name of the entity manager factory object.

      MicroTxConfig.initEntityManagerFactory(emf);
    • When your application connects with multiple resource managers, you must pass the following parameters while calling MicroTxConfig.initEntityManagerFactory().

      MicroTxConfig.initEntityManagerFactory(emf, departmentDataSource, ORCL1-8976-9776-9873);

      Where,

      • emf is the entity manager factory object that you have created, and then you pass it to the MicroTx library.
      • departmentDataSource is the name of the data source that you have created in the above sample code before calling MicroTxConfig.initEntityManagerFactory().
      • ORCL1-8976-9776-9873 is the resource manager ID (RMID).
  5. Insert the following lines in the code of the participant service so that the application uses the connection passed by the MicroTx client library. The following code in the participant application injects the connection object that is created by the MicroTx client library.
    • If you use a single resource manager with a single application, inject an EntityManager object as shown in the following code sample, where microTxEntityManager is the name of the entity manager factory object.

      @Autowired
      @Qualifier("microTxEntityManager")
      @Lazy
      private EntityManager entityManager;
    • When you use multiple resource managers with your application, inject an EntityManager object for every resource manager as shown in the following code sample.

      //Initialize the application context
      private ApplicationContext applicationContext;
      //Entity Manager object for XA-compliant resource manager 1
      private EntityManager entityManager;
      //Entity Manager object for XA-compliant resource manager 2
      private EntityManager creditEntityManager;
      @Autowired
          public AccountService(ApplicationContext applicationContext) {
              this.applicationContext = applicationContext;
              try {
                  entityManager =  (EntityManager) applicationContext.getBean("microTxEntityManager", "departmentDataSource");
                  creditEntityManager =  (EntityManager) applicationContext.getBean("microTxEntityManager", "creditDataSource");
              } catch (ClassCastException ex) {
                  LOG.info(ex.getMessage());
              }
          }

      Where, microTxEntityManager is the entity manager factory object and departmentDataSource and creditDataSource are data source objects that you have created in the previous step. The earlier code sample provides details about departmentDataSource. Provide information for other resource managers, such as creditDataSource in a similar way.

      Repeat this step for every resource manager.

  6. In your application code, inject the entity manager object that you have passed to the MicroTx library. Use the entity manager object in your application code based on your business logic, and then use this object to connect to the database.
  7. Save the changes.
If there are multiple transaction participant services, then complete these steps for all the participant services.