I usually post about Persistence on Google+ - you can follow me there:
1. Overview
In this tutorial we’ll implement a simple Spring configuration for a Spring Data JPA system with multiple databases.
2. The Entities
First – let’s create two simple entities – each living in a separate database.
Here is the first entity “User“:
package org.baeldung.persistence.multiple.model.user; @Entity @Table(schema = "spring_jpa_user") public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; private String name; @Column(unique = true, nullable = false) private String email; private int age; }
And the second entity – “Product“:
package org.baeldung.persistence.multiple.model.product; @Entity @Table(schema = "spring_jpa_product") public class Product { @Id private int id; private String name; private double price; }
As you can see, the two entities are also placed in independent packages – this will be important as we move into the configuration.
3. The JPA Repositories
Next – let’s take a look at our two JPA repositories – UserRepository:
package org.baeldung.persistence.multiple.dao.user; public interface UserRepository extends JpaRepository<User, Integer> { }
And ProductRepository:
package org.baeldung.persistence.multiple.dao.product; public interface ProductRepository extends JpaRepository<Product, Integer> { }
Note, again how we created these two repositories in different packages.
4. Configure JPA with Java
Next – let’s get to the actual Spring configuration. We’ll start by setting up 2 configuration classes – one for the User and the other for the Product.
In each one of this configuration classes, we’ll need to define the following:
- User DataSource
- User EntityManagerFactory (userEntityManager)
- User TransactionManager (userTransactionManager)
Let’s start by looking the the User configuration:
@Configuration @PropertySource({ "classpath:persistence-multiple-db.properties" }) @EnableJpaRepositories( basePackages = "org.baeldung.persistence.multiple.dao.user", entityManagerFactoryRef = "userEntityManager", transactionManagerRef = "userTransactionManager" ) public class UserConfig { @Autowired private Environment env; @Bean @Primary public LocalContainerEntityManagerFactoryBean userEntityManager() { LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(userDataSource()); em.setPackagesToScan(new String[] { "org.baeldung.persistence.multiple.model.user" }); HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); em.setJpaVendorAdapter(vendorAdapter); HashMap<String, Object> properties = new HashMap<String, Object>(); properties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto")); properties.put("hibernate.dialect", env.getProperty("hibernate.dialect")); em.setJpaPropertyMap(properties); return em; } @Primary @Bean public DataSource userDataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName")); dataSource.setUrl(env.getProperty("user.jdbc.url")); dataSource.setUsername(env.getProperty("jdbc.user")); dataSource.setPassword(env.getProperty("jdbc.pass")); return dataSource; } @Primary @Bean public PlatformTransactionManager userTransactionManager() { JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(userEntityManager().getObject()); return transactionManager; } }
Notice how we’re using the userTransactionManager as our Primary TransactionManager – by annotating the bean definition with @Primary. That’s helpful whenever we’re going to implicitly or explicitly inject the transaction manager without specifying which one by name.
Next, let’s discuss ProductConfig - where we define similar beans:
@Configuration @PropertySource({ "classpath:persistence-multiple-db.properties" }) @EnableJpaRepositories( basePackages = "org.baeldung.persistence.multiple.dao.product", entityManagerFactoryRef = "productEntityManager", transactionManagerRef = "productTransactionManager" ) public class ProductConfig { @Autowired private Environment env; @Bean public LocalContainerEntityManagerFactoryBean productEntityManager() { LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(productDataSource()); em.setPackagesToScan(new String[] { "org.baeldung.persistence.multiple.model.product" }); HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); em.setJpaVendorAdapter(vendorAdapter); HashMap<String, Object> properties = new HashMap<String, Object>(); properties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto")); properties.put("hibernate.dialect", env.getProperty("hibernate.dialect")); em.setJpaPropertyMap(properties); return em; } @Bean public DataSource productDataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName")); dataSource.setUrl(env.getProperty("product.jdbc.url")); dataSource.setUsername(env.getProperty("jdbc.user")); dataSource.setPassword(env.getProperty("jdbc.pass")); return dataSource; } @Bean public PlatformTransactionManager productTransactionManager() { JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(productEntityManager().getObject()); return transactionManager; } }
5. Simple Test
Finally – let’s test our configurations.
We will try a simple test by creating an instance of each entity and make sure it is created – as in the following example:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { UserConfig.class, ProductConfig.class }) @TransactionConfiguration public class JPAMultipleDBTest { @Autowired private UserRepository userRepository; @Autowired private ProductRepository productRepository; @Test @Transactional("userTransactionManager") public void whenCreatingUser_thenCreated() { User user = new User(); user.setName("John"); user.setEmail("john@test.com"); user.setAge(20); user = userRepository.save(user); assertNotNull(userRepository.findOne(user.getId())); } @Test @Transactional("userTransactionManager") public void whenCreatingUsersWithSameEmail_thenRollback() { User user1 = new User(); user1.setName("John"); user1.setEmail("john@test.com"); user1.setAge(20); user1 = userRepository.save(user1); assertNotNull(userRepository.findOne(user1.getId())); User user2 = new User(); user2.setName("Tom"); user2.setEmail("john@test.com"); user2.setAge(10); try { user2 = userRepository.save(user2); } catch (DataIntegrityViolationException e) { } assertNull(userRepository.findOne(user2.getId())); } @Test @Transactional("productTransactionManager") public void whenCreatingProduct_thenCreated() { Product product = new Product(); product.setName("Book"); product.setId(2); product.setPrice(20); product = productRepository.save(product); assertNotNull(productRepository.findOne(product.getId())); } }
6. Conclusion
This article was a practical overview of how to configure your Spring Data JPA project to use multiple databases.
The full implementation of this article can be found in the github project – this is an Eclipse based project, so it should be easy to import and run as it is.