Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Boot with application managed persistence context

I am trying to migrate an application from EJB3 + JTA + JPA (EclipseLink). Currently, this application makes use of application managed persistent context due to an unknown number of databases on design time.

The application managed persistent context allows us to control how to create EntityManager (e.g. supply different datasources JNDI to create proper EntityManager for specific DB on runtime).

E.g.

    Map properties = new HashMap();

    properties.put(PersistenceUnitProperties.TRANSACTION_TYPE, "JTA");

    //the datasource JNDI is by configuration and without prior knowledge about the number of databases
    //currently, DB JNDI are stored in a externalized file
    //the datasource is setup by operation team
    properties.put(PersistenceUnitProperties.JTA_DATASOURCE, "datasource-jndi");

    properties.put(PersistenceUnitProperties.CACHE_SHARED_DEFAULT, "false");
    properties.put(PersistenceUnitProperties.SESSION_NAME, "xxx");

    //create the proper EntityManager for connect to database decided on runtime                

    EntityManager em = Persistence.createEntityManagerFactory("PU1", properties).createEntityManager();     

    //query or update DB
    em.persist(entity);
    em.createQuery(...).executeUpdate();

When deployed in a EJB container (e.g. WebLogic), with proper TransactionAttribute (e.g. TransactionAttributeType.REQUIRED), the container will take care of the transaction start/end/rollback.

Now, I am trying to migrate this application to Spring Boot. The problem I encounter is that there is no transaction started even after I annotate the method with @Transactional(propagation = Propagation.REQUIRED).

The Spring application is packed as an executable JAR file and run with embadded Tomcat.

When I try to execute those update APIs, e.g. EntityManager.persist(..), EclipseLink always complains about:

javax.persistence.TransactionRequiredException: 'No transaction is currently active'

Sample code below:


    //for data persistence
    @Service
    class DynamicServiceImpl implements DynamicService {

        //attempt to start a transaction
        @Transactional(propagation = Propagation.REQUIRED)
        public void saveData(DbJndi, EntityA){

            //this return false that no transaction started
            TransactionSynchronizationManager.isActualTransactionActive();

            //create an EntityManager based on the input DbJndi to dynamically
            //determine which DB to save the data
            EntityManager em = createEm(DbJndi);

            //save the data 
            em.persist(EntityA);
        }
    }

    //restful service
    @RestController
    class RestController{

        @Autowired
        DynamicService service;

        @RequestMapping( value = "/saveRecord", method = RequestMethod.POST)
        public @ResponseBody String saveRecord(){
             //save data
             service.saveData(...)
        }
    }

    //startup application
    @SpringBootApplication
    class TestApp {

        public static void main(String[] args) {
            SpringApplication.run(TestApp.class, args);
        }
    }

    persistence.xml
    -------------------------------------------

    &ltpersistence-unit name="PU1" transaction-type="JTA">

        &ltproperties>

            &lt!-- comment for spring to handle transaction??? -->
            &lt!--property name="eclipselink.target-server" value="WebLogic_10"/ -->
        &lt/properties>

    &lt/persistence-unit>

    -------------------------------------------

    application.properties (just 3 lines of config)
    -------------------------------------------
    spring.jta.enabled=true 
    spring.jta.log-dir=spring-test # Transaction logs directory.
    spring.jta.transaction-manager-id=spring-test
    -------------------------------------------

My usage pattern does not follow most typical use cases (e.g. with known number of DBs - Spring + JPA + multiple persistence units: Injecting EntityManager).

Can anybody give me advice on how to solve this issue?

Is there anybody who has ever hit this situation that the DBs are not known in design time?

Thank you.

like image 827
Paul.Luk Avatar asked Sep 23 '26 15:09

Paul.Luk


1 Answers

I finally got it work with:

  1. Enable tomcat JNDI and create the datasource JNDI to each DS programmatically
  2. Add transaction stuff

    • com.atomikos:transactions-eclipselink:3.9.3 (my project uses eclipselink instead of hibernate)

    • org.springframework.boot:spring-boot-starter-jta-atomikos

    • org.springframework:spring-tx
like image 104
Paul.Luk Avatar answered Sep 27 '26 09:09

Paul.Luk



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!