Configuring Multiple JPA Entity Managers In Spring Boot

This blog will demonstrate how to setup multiple entity managers in Spring to connect to different data sources. The solution here also supports Spring Data.

Update Maven Pom file

Include Spring Boot dependency for Spring Data:

 <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-data-jpa</artifactId>
 </dependency>

Disable DataSourceAutoConfiguration

Since we are setting up the data sources, disable the auto configuration in Spring Boot

@Configuration
@ComponentScan
@EnableAutoConfiguration (exclude = {  DataSourceAutoConfiguration.class })
public class Application {
   ...

Configure Primary Entity Manager

Below is the Java configuration for the primary entity manager

@Profile("!test")          // 1
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "au.com.myblog.dao", entityManagerFactoryRef = "entityManager", transactionManagerRef = "transactionManager")        // 2
public class PrimaryMysqlDBConfiguration {
     @Bean(name = "dataSource")      // 3
     @Primary
     @ConfigurationProperties(prefix = "primary.datasource.mysql")
     public DataSource mysqlDataSource() {
          return DataSourceBuilder.create().build();
     }
    @PersistenceContext(unitName = "primary")   // 4
     @Primary
     @Bean(name = "entityManager")
     public LocalContainerEntityManagerFactoryBean mySqlEntityManagerFactory(EntityManagerFactoryBuilder builder) {
          return builder.dataSource(mysqlDataSource()).persistenceUnit("primary").properties(jpaProperties())
                    .packages("au.com.myblog.domain").build();
      }
     private Map<String, Object> jpaProperties() {
          Map<String, Object> props = new HashMap<>();
          props.put("hibernate.ejb.naming_strategy", new SpringNamingStrategy());
          return props;
      }
}

Note:

  1. @Profile annotation to use this configuration only for non test profile. This allows me to set a different datasource, e.g. H2, when running tests.
  2. @EnableJpaRepositories is used for Spring Data. Note we are using the default transaction manager setup in Spring
  3. Bean for primary datasource. The @ConfigurationProperties annotation specifies the prefix of the properties to use by this datasource. For the example here, the application.properties file should include properties like below:
    # DB Connection
    primary.datasource.mysql.url=jdbc:log4jdbc:mysql://localhost/bpmn_service
    primary.datasource.mysql.username=root
    primary.datasource.mysql.password=root
    primary.datasource.mysql.driver-class-name=net.sf.log4jdbc.DriverSpy
  4. Persistence unit name should be setup in the EntityManager bean as shown here.

Configure Secondary Entity Manager

Configuration of the secondary entity manager is very similar to that of the primary.The only thing is we have to define a new transaction manager. Make sure a different prefix is set in the configuration properties of the data source. Also, a different persistent unit name should be used.

@Profile("!test")
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "au.com.myblog.dao", entityManagerFactoryRef = "secondaryMySqlEntityManager", transactionManagerRef = "secondaryTransactionManager")
public class SecondaryMysqlDBConfiguration {
      @Bean
      @ConfigurationProperties(prefix = "secondary.datasource.mysql")
       public DataSource mysqlDataSource() {
            return DataSourceBuilder.create().build();
       }
      @PersistenceContext(unitName = "secondary")
      @Bean(name = "secondaryMySqlEntityManager")
      public LocalContainerEntityManagerFactoryBean mySqlEntityManagerFactory(EntityManagerFactoryBuilder builder) {
           return  builder.dataSource(mysqlDataSource()).properties(jpaProperties()).persistenceUnit("secondary").packages("au.com.myblog.domain").build();
       }
      @Bean(name = "secondaryTransactionManager")
       public PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder) {
             JpaTransactionManager tm = new JpaTransactionManager();
             tm.setEntityManagerFactory(mySqlEntityManagerFactory(builder).getObject());
             tm.setDataSource(mysqlDataSource());
             return tm;
       }
       private Map<String, Object> jpaProperties() {
             Map<String, Object> props = new HashMap<>();
            // naming strategy to put underscores instead of camel case
            // as per auto JPA Configuration
            props.put("hibernate.ejb.naming_strategy", new SpringNamingStrategy());
            props.put("hibernate.hbm2ddl.auto", "update");
            return props;
       }

Note the setup above assumes the primary and secondary data sources are not used together in a single transaction. Hence we can use 2 independent transaction managers.

Specify which Entity Manager to use

Finally, make sure that you specify the name of the secondary transaction in the spring @Transactional annotation:

@Transactional(value = "secondaryTransactionManager")

Also, add the @PersistenceContext annotation with the unit name as defined in the configuration when injecting entity managers;

 @PersistenceContext(unitName = "secondary")
 private EntityManager entityManager;

That’s it!

About Raymond Lee
Professional Java/EE Developer, software development technology enthusiast.

Comments are closed.