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“:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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“:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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:
1
2
3
|
package
org.baeldung.persistence.multiple.dao.user;
public
interface
UserRepository
extends
JpaRepository<User, Integer> { }
|
And ProductRepository:
1
2
3
|
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
@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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
@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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
@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.