├── .gitignore ├── pom.xml └── src ├── main ├── java │ └── demo │ │ ├── DemoApplication.java │ │ ├── customer │ │ ├── CustomerConfig.java │ │ └── domain │ │ │ ├── Customer.java │ │ │ └── CustomerRepository.java │ │ └── order │ │ ├── OrderConfig.java │ │ └── domain │ │ ├── Order.java │ │ └── OrderRepository.java └── resources │ └── application.properties └── test └── java └── demo ├── DemoApplicationTests.java ├── customer └── domain │ └── CustomerTest.java └── order └── domain └── OrderTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | *.ipr 4 | *.iws 5 | target -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.1.2 9 | 10 | 11 | org.test 12 | demo-multi-entity-managers 13 | 0.0.1-SNAPSHOT 14 | Multi entity managers demo 15 | 16 | Demonstrate how to configure Spring Boot to use several entity managers 17 | 18 | 19 | 0.0.39 20 | 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-data-jpa 26 | 27 | 28 | 29 | com.h2database 30 | h2 31 | runtime 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-configuration-processor 37 | true 38 | 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-test 43 | test 44 | 45 | 46 | 47 | 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-maven-plugin 52 | 53 | 54 | io.spring.javaformat 55 | spring-javaformat-maven-plugin 56 | ${spring-javaformat.version} 57 | 58 | 59 | validate 60 | 61 | validate 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/main/java/demo/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; 6 | 7 | @SpringBootApplication(exclude = HibernateJpaAutoConfiguration.class) 8 | public class DemoApplication { 9 | 10 | public static void main(String[] args) { 11 | SpringApplication.run(DemoApplication.class, args); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/demo/customer/CustomerConfig.java: -------------------------------------------------------------------------------- 1 | package demo.customer; 2 | 3 | import com.zaxxer.hikari.HikariDataSource; 4 | import demo.customer.domain.Customer; 5 | import jakarta.persistence.EntityManagerFactory; 6 | 7 | import org.springframework.beans.factory.ObjectProvider; 8 | import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; 9 | import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties; 10 | import org.springframework.boot.context.properties.ConfigurationProperties; 11 | import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; 12 | import org.springframework.context.annotation.Bean; 13 | import org.springframework.context.annotation.Configuration; 14 | import org.springframework.context.annotation.Primary; 15 | import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 16 | import org.springframework.orm.jpa.JpaTransactionManager; 17 | import org.springframework.orm.jpa.JpaVendorAdapter; 18 | import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; 19 | import org.springframework.orm.jpa.persistenceunit.PersistenceUnitManager; 20 | import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter; 21 | import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; 22 | 23 | @Configuration 24 | @EnableJpaRepositories(entityManagerFactoryRef = "customerEntityManager", 25 | transactionManagerRef = "customerTransactionManager", basePackageClasses = Customer.class) 26 | public class CustomerConfig { 27 | 28 | private final PersistenceUnitManager persistenceUnitManager; 29 | 30 | public CustomerConfig(ObjectProvider persistenceUnitManager) { 31 | this.persistenceUnitManager = persistenceUnitManager.getIfAvailable(); 32 | } 33 | 34 | @Bean 35 | @ConfigurationProperties("app.customer.jpa") 36 | public JpaProperties customerJpaProperties() { 37 | return new JpaProperties(); 38 | } 39 | 40 | @Bean 41 | @ConfigurationProperties("app.customer.datasource") 42 | public DataSourceProperties customerDataSourceProperties() { 43 | return new DataSourceProperties(); 44 | } 45 | 46 | @Bean 47 | @ConfigurationProperties(prefix = "app.customer.datasource.properties") 48 | public HikariDataSource customerDataSource() { 49 | return customerDataSourceProperties().initializeDataSourceBuilder().type(HikariDataSource.class).build(); 50 | } 51 | 52 | @Bean 53 | public LocalContainerEntityManagerFactoryBean customerEntityManager(JpaProperties customerJpaProperties) { 54 | EntityManagerFactoryBuilder builder = createEntityManagerFactoryBuilder(customerJpaProperties); 55 | return builder.dataSource(customerDataSource()).packages(Customer.class).persistenceUnit("customersDs").build(); 56 | } 57 | 58 | @Bean 59 | @Primary 60 | public JpaTransactionManager customerTransactionManager(EntityManagerFactory customerEntityManager) { 61 | return new JpaTransactionManager(customerEntityManager); 62 | } 63 | 64 | private EntityManagerFactoryBuilder createEntityManagerFactoryBuilder(JpaProperties customerJpaProperties) { 65 | JpaVendorAdapter jpaVendorAdapter = createJpaVendorAdapter(customerJpaProperties); 66 | return new EntityManagerFactoryBuilder(jpaVendorAdapter, customerJpaProperties.getProperties(), 67 | this.persistenceUnitManager); 68 | } 69 | 70 | private JpaVendorAdapter createJpaVendorAdapter(JpaProperties jpaProperties) { 71 | AbstractJpaVendorAdapter adapter = new HibernateJpaVendorAdapter(); 72 | adapter.setShowSql(jpaProperties.isShowSql()); 73 | if (jpaProperties.getDatabase() != null) { 74 | adapter.setDatabase(jpaProperties.getDatabase()); 75 | } 76 | if (jpaProperties.getDatabasePlatform() != null) { 77 | adapter.setDatabasePlatform(jpaProperties.getDatabasePlatform()); 78 | } 79 | adapter.setGenerateDdl(jpaProperties.isGenerateDdl()); 80 | return adapter; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/demo/customer/domain/Customer.java: -------------------------------------------------------------------------------- 1 | package demo.customer.domain; 2 | 3 | import java.io.Serializable; 4 | 5 | import jakarta.persistence.Column; 6 | import jakarta.persistence.Entity; 7 | import jakarta.persistence.GeneratedValue; 8 | import jakarta.persistence.Id; 9 | 10 | @Entity 11 | public class Customer implements Serializable { 12 | 13 | private static final long serialVersionUID = 1L; 14 | 15 | @Id 16 | @GeneratedValue 17 | private Long id; 18 | 19 | @Column(nullable = false) 20 | private String firstName; 21 | 22 | @Column(nullable = false) 23 | private String lastName; 24 | 25 | public Customer() { 26 | } 27 | 28 | public Customer(String firstName, String lastName) { 29 | this.firstName = firstName; 30 | this.lastName = lastName; 31 | } 32 | 33 | public Long getId() { 34 | return id; 35 | } 36 | 37 | public String getFirstName() { 38 | return firstName; 39 | } 40 | 41 | public void setFirstName(String firstName) { 42 | this.firstName = firstName; 43 | } 44 | 45 | public String getLastName() { 46 | return lastName; 47 | } 48 | 49 | public void setLastName(String lastName) { 50 | this.lastName = lastName; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/demo/customer/domain/CustomerRepository.java: -------------------------------------------------------------------------------- 1 | package demo.customer.domain; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | 5 | public interface CustomerRepository extends JpaRepository { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/demo/order/OrderConfig.java: -------------------------------------------------------------------------------- 1 | package demo.order; 2 | 3 | import com.zaxxer.hikari.HikariDataSource; 4 | import demo.order.domain.Order; 5 | import jakarta.persistence.EntityManagerFactory; 6 | 7 | import org.springframework.beans.factory.ObjectProvider; 8 | import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; 9 | import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties; 10 | import org.springframework.boot.context.properties.ConfigurationProperties; 11 | import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; 12 | import org.springframework.context.annotation.Bean; 13 | import org.springframework.context.annotation.Configuration; 14 | import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 15 | import org.springframework.orm.jpa.JpaTransactionManager; 16 | import org.springframework.orm.jpa.JpaVendorAdapter; 17 | import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; 18 | import org.springframework.orm.jpa.persistenceunit.PersistenceUnitManager; 19 | import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter; 20 | import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; 21 | 22 | @Configuration 23 | @EnableJpaRepositories(entityManagerFactoryRef = "orderEntityManager", 24 | transactionManagerRef = "orderTransactionManager", basePackageClasses = Order.class) 25 | public class OrderConfig { 26 | 27 | private final PersistenceUnitManager persistenceUnitManager; 28 | 29 | public OrderConfig(ObjectProvider persistenceUnitManager) { 30 | this.persistenceUnitManager = persistenceUnitManager.getIfAvailable(); 31 | } 32 | 33 | @Bean 34 | @ConfigurationProperties("app.order.jpa") 35 | public JpaProperties orderJpaProperties() { 36 | return new JpaProperties(); 37 | } 38 | 39 | @Bean 40 | @ConfigurationProperties("app.order.datasource") 41 | public DataSourceProperties orderDataSourceProperties() { 42 | return new DataSourceProperties(); 43 | } 44 | 45 | @Bean 46 | @ConfigurationProperties(prefix = "app.order.datasource.properties") 47 | public HikariDataSource orderDataSource() { 48 | return orderDataSourceProperties().initializeDataSourceBuilder().type(HikariDataSource.class).build(); 49 | } 50 | 51 | @Bean 52 | public LocalContainerEntityManagerFactoryBean orderEntityManager(JpaProperties orderJpaProperties) { 53 | EntityManagerFactoryBuilder builder = createEntityManagerFactoryBuilder(orderJpaProperties); 54 | return builder.dataSource(orderDataSource()).packages(Order.class).persistenceUnit("ordersDs").build(); 55 | } 56 | 57 | @Bean 58 | public JpaTransactionManager orderTransactionManager(EntityManagerFactory orderEntityManager) { 59 | return new JpaTransactionManager(orderEntityManager); 60 | } 61 | 62 | private EntityManagerFactoryBuilder createEntityManagerFactoryBuilder(JpaProperties orderJpaProperties) { 63 | JpaVendorAdapter jpaVendorAdapter = createJpaVendorAdapter(orderJpaProperties); 64 | return new EntityManagerFactoryBuilder(jpaVendorAdapter, orderJpaProperties.getProperties(), 65 | this.persistenceUnitManager); 66 | } 67 | 68 | private JpaVendorAdapter createJpaVendorAdapter(JpaProperties jpaProperties) { 69 | AbstractJpaVendorAdapter adapter = new HibernateJpaVendorAdapter(); 70 | adapter.setShowSql(jpaProperties.isShowSql()); 71 | if (jpaProperties.getDatabase() != null) { 72 | adapter.setDatabase(jpaProperties.getDatabase()); 73 | } 74 | if (jpaProperties.getDatabasePlatform() != null) { 75 | adapter.setDatabasePlatform(jpaProperties.getDatabasePlatform()); 76 | } 77 | adapter.setGenerateDdl(jpaProperties.isGenerateDdl()); 78 | return adapter; 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/demo/order/domain/Order.java: -------------------------------------------------------------------------------- 1 | package demo.order.domain; 2 | 3 | import java.io.Serializable; 4 | import java.util.Date; 5 | 6 | import jakarta.persistence.Column; 7 | import jakarta.persistence.Entity; 8 | import jakarta.persistence.GeneratedValue; 9 | import jakarta.persistence.Id; 10 | import jakarta.persistence.Table; 11 | import jakarta.persistence.Temporal; 12 | import jakarta.persistence.TemporalType; 13 | 14 | @Entity 15 | @Table(name = "CUSTOMER_ORDER") 16 | public class Order implements Serializable { 17 | 18 | private static final long serialVersionUID = 1L; 19 | 20 | @Id 21 | @GeneratedValue 22 | private Long id; 23 | 24 | @Column(nullable = false) 25 | private Long customerId; 26 | 27 | @Column(nullable = false) 28 | @Temporal(TemporalType.DATE) 29 | private Date orderDate; 30 | 31 | public Order() { 32 | } 33 | 34 | public Order(Long customerId, Date orderDate) { 35 | this.customerId = customerId; 36 | this.orderDate = orderDate; 37 | } 38 | 39 | public Long getId() { 40 | return id; 41 | } 42 | 43 | public Long getCustomerId() { 44 | return customerId; 45 | } 46 | 47 | public void setCustomerId(Long customerId) { 48 | this.customerId = customerId; 49 | } 50 | 51 | public Date getOrderDate() { 52 | return orderDate; 53 | } 54 | 55 | public void setOrderDate(Date orderDate) { 56 | this.orderDate = orderDate; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/demo/order/domain/OrderRepository.java: -------------------------------------------------------------------------------- 1 | package demo.order.domain; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | 5 | public interface OrderRepository extends JpaRepository { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | app.customer.datasource.url=jdbc:h2:mem:customers;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE 2 | app.customer.jpa.properties.hibernate.hbm2ddl.auto=create 3 | 4 | app.order.datasource.url=jdbc:h2:mem:orders;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE 5 | app.order.jpa.properties.hibernate.hbm2ddl.auto=create 6 | -------------------------------------------------------------------------------- /src/test/java/demo/DemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import java.sql.DatabaseMetaData; 4 | import java.sql.ResultSet; 5 | import java.util.Date; 6 | 7 | import javax.sql.DataSource; 8 | 9 | import demo.customer.domain.Customer; 10 | import demo.customer.domain.CustomerRepository; 11 | import demo.order.domain.Order; 12 | import demo.order.domain.OrderRepository; 13 | import org.junit.jupiter.api.Test; 14 | 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.beans.factory.annotation.Qualifier; 17 | import org.springframework.boot.test.context.SpringBootTest; 18 | import org.springframework.jdbc.core.ConnectionCallback; 19 | import org.springframework.jdbc.core.JdbcTemplate; 20 | 21 | import static org.assertj.core.api.Assertions.assertThat; 22 | 23 | @SpringBootTest 24 | class DemoApplicationTests { 25 | 26 | @Autowired 27 | @Qualifier("customerDataSource") 28 | private DataSource customerDataSource; 29 | 30 | @Autowired 31 | private CustomerRepository customerRepository; 32 | 33 | @Autowired 34 | @Qualifier("orderDataSource") 35 | private DataSource orderDataSource; 36 | 37 | @Autowired 38 | private OrderRepository orderRepository; 39 | 40 | @Test 41 | void customerDataSourceIsInitializedProperly() { 42 | JdbcTemplate customerTemplate = new JdbcTemplate(this.customerDataSource); 43 | assertThat(hasTable(customerTemplate, "CUSTOMER")).isTrue(); 44 | assertThat(hasTable(customerTemplate, "CUSTOMER_ORDER")).isFalse(); 45 | } 46 | 47 | @Test 48 | void customerRepositoryConfiguredProperly() { 49 | JdbcTemplate customerTemplate = new JdbcTemplate(this.customerDataSource); 50 | int count = countItem(customerTemplate, "CUSTOMER"); 51 | Customer customer = new Customer("John", "Smith"); 52 | this.customerRepository.save(customer); 53 | assertThat(countItem(customerTemplate, "CUSTOMER")).isEqualTo(++count); 54 | } 55 | 56 | @Test 57 | void orderDataSourceIsInitializedProperly() { 58 | JdbcTemplate orderTemplate = new JdbcTemplate(this.orderDataSource); 59 | assertThat(hasTable(orderTemplate, "CUSTOMER_ORDER")).isTrue(); 60 | assertThat(hasTable(orderTemplate, "CUSTOMER")).isFalse(); 61 | } 62 | 63 | @Test 64 | void orderRepositoryConfiguredProperly() { 65 | JdbcTemplate orderTemplate = new JdbcTemplate(this.orderDataSource); 66 | int count = countItem(orderTemplate, "CUSTOMER_ORDER"); 67 | Order order = new Order(123L, new Date()); 68 | this.orderRepository.save(order); 69 | assertThat(countItem(orderTemplate, "CUSTOMER_ORDER")).isEqualTo(++count); 70 | } 71 | 72 | private boolean hasTable(JdbcTemplate jdbcTemplate, String tableName) { 73 | return jdbcTemplate.execute((ConnectionCallback) connection -> { 74 | DatabaseMetaData md = connection.getMetaData(); 75 | try (ResultSet rs = md.getTables(null, null, tableName, new String[] { "TABLE" })) { 76 | return (rs.next()); 77 | } 78 | }); 79 | } 80 | 81 | private int countItem(JdbcTemplate jdbcTemplate, String tableName) { 82 | return jdbcTemplate.queryForObject(String.format("SELECT COUNT(*) from %s", tableName), Integer.class); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/test/java/demo/customer/domain/CustomerTest.java: -------------------------------------------------------------------------------- 1 | package demo.customer.domain; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; 7 | 8 | import static org.assertj.core.api.Assertions.assertThat; 9 | 10 | @DataJpaTest // Slice test does not use CustomerConfig 11 | class CustomerTest { 12 | 13 | @Autowired 14 | private CustomerRepository customerRepository; 15 | 16 | @Test 17 | void save() { 18 | Customer customer = new Customer("John", "Smith"); 19 | assertThat(customer.getId()).isNull(); 20 | this.customerRepository.save(customer); 21 | assertThat(customer.getId()).isNotNull(); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/demo/order/domain/OrderTest.java: -------------------------------------------------------------------------------- 1 | package demo.order.domain; 2 | 3 | import java.util.Date; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; 9 | 10 | import static org.assertj.core.api.Assertions.assertThat; 11 | 12 | @DataJpaTest // Slice test does not use OrderConfig 13 | class OrderTest { 14 | 15 | @Autowired 16 | private OrderRepository orderRepository; 17 | 18 | @Test 19 | void save() { 20 | Order order = new Order(123L, new Date()); 21 | assertThat(order.getId()).isNull(); 22 | this.orderRepository.save(order); 23 | assertThat(order.getId()).isNotNull(); 24 | } 25 | 26 | } 27 | --------------------------------------------------------------------------------