├── .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 |
--------------------------------------------------------------------------------