1. 概述
在本文中,我们将通过一个简单的 Spring 配置示例,演示如何在一个 Spring Data JPA 项目中配置使用多个数据库。
这种场景在企业级应用开发中非常常见,例如:用户信息和商品信息分别存储在两个独立的数据库中,以提高系统解耦性和可维护性。
2. 实体类定义
我们为每个数据库分别定义一个实体类,并将其放置在不同的包路径下,以便后续配置时进行隔离扫描。
User 实体类(用户数据库)
package com.baeldung.multipledb.model.user;
import javax.persistence.*;
@Entity
@Table(schema = "users")
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;
// getter / setter 省略
}
Product 实体类(商品数据库)
package com.baeldung.multipledb.model.product;
import javax.persistence.*;
@Entity
@Table(schema = "products")
public class Product {
@Id
private int id;
private String name;
private double price;
// getter / setter 省略
}
⚠️ 注意:两个实体类分别位于 user
和 product
包下,这是后续配置多数据源的关键。
3. JPA Repository 接口
为每个实体类定义一个对应的 Repository 接口,同样也放置在不同的包路径下。
UserRepository
package com.baeldung.multipledb.dao.user;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Integer> {
}
ProductRepository
package com.baeldung.multipledb.dao.product;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ProductRepository extends JpaRepository<Product, Integer> {
}
✅ 建议:将 Repository 按功能模块划分包路径,有助于后续多数据源的配置和管理。
4. Spring 多数据源配置(Java 配置方式)
为了支持多个数据库,我们需要为每个数据库单独配置以下组件:
DataSource
:数据库连接池EntityManagerFactory
:JPA 实体管理工厂PlatformTransactionManager
:事务管理器
我们通过两个独立的配置类分别管理用户和商品相关的数据库连接。
4.1 用户数据库配置类:PersistenceUserConfiguration
@Configuration
@PropertySource({ "classpath:persistence-multiple-db.properties" })
@EnableJpaRepositories(
basePackages = "com.baeldung.multipledb.dao.user",
entityManagerFactoryRef = "userEntityManager",
transactionManagerRef = "userTransactionManager"
)
public class PersistenceUserConfiguration {
@Autowired
private Environment env;
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean userEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(userDataSource());
em.setPackagesToScan("com.baeldung.multipledb.model.user");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
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;
}
}
4.2 商品数据库配置类:PersistenceProductConfiguration
@Configuration
@PropertySource({ "classpath:persistence-multiple-db.properties" })
@EnableJpaRepositories(
basePackages = "com.baeldung.multipledb.dao.product",
entityManagerFactoryRef = "productEntityManager",
transactionManagerRef = "productTransactionManager"
)
public class PersistenceProductConfiguration {
@Autowired
private Environment env;
@Bean
public LocalContainerEntityManagerFactoryBean productEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(productDataSource());
em.setPackagesToScan("com.baeldung.multipledb.model.product");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
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;
}
}
📌 说明:
- 使用
@Primary
注解标记主数据源(用户数据库)的事务管理器和数据源,避免 Spring 自动注入时出现冲突。 - 每个配置类都通过
@EnableJpaRepositories
指定了扫描路径、EntityManagerFactory 和 TransactionManager 的 Bean 名称,确保不同数据源之间完全隔离。
5. 验证测试
我们编写几个简单的单元测试来验证两个数据库是否都能正常工作。
@RunWith(SpringRunner.class)
@SpringBootTest
@EnableTransactionManagement
public class JpaMultipleDBIntegrationTest {
@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@example.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@example.com");
user1.setAge(20);
user1 = userRepository.save(user1);
assertNotNull(userRepository.findOne(user1.getId()));
User user2 = new User();
user2.setName("Tom");
user2.setEmail("john@example.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()));
}
}
✅ 测试说明:
- 每个测试方法都通过
@Transactional
明确指定使用的事务管理器。 - 可以验证用户和商品是否能正确插入、事务是否生效、约束是否生效(如 email 唯一性)。
6. Spring Boot 中的简化配置
在 Spring Boot 中,我们可以通过 @ConfigurationProperties
简化数据源的配置,而无需手动设置每个属性。
6.1 配置文件(persistence-multiple-db-boot.properties)
spring.datasource.url=jdbc:mysql://localhost:3306/users_db
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.second-datasource.url=jdbc:mysql://localhost:3306/products_db
spring.second-datasource.username=root
spring.second-datasource.password=root
spring.second-datasource.driver-class-name=com.mysql.cj.jdbc.Driver
hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
hibernate.hbm2ddl.auto=update
6.2 用户数据源配置类
@Configuration
@PropertySource({"classpath:persistence-multiple-db-boot.properties"})
@EnableJpaRepositories(
basePackages = "com.baeldung.multipledb.dao.user",
entityManagerFactoryRef = "userEntityManager",
transactionManagerRef = "userTransactionManager"
)
public class PersistenceUserAutoConfiguration {
@Primary
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource userDataSource() {
return DataSourceBuilder.create().build();
}
@Primary
@Bean
public LocalContainerEntityManagerFactoryBean userEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(userDataSource());
em.setPackagesToScan("com.baeldung.multipledb.model.user");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto", "update");
properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL8Dialect");
em.setJpaPropertyMap(properties);
return em;
}
@Primary
@Bean
public PlatformTransactionManager userTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(userEntityManager().getObject());
return transactionManager;
}
}
6.3 商品数据源配置类
@Configuration
@PropertySource({"classpath:persistence-multiple-db-boot.properties"})
@EnableJpaRepositories(
basePackages = "com.baeldung.multipledb.dao.product",
entityManagerFactoryRef = "productEntityManager",
transactionManagerRef = "productTransactionManager"
)
public class PersistenceProductAutoConfiguration {
@Bean
@ConfigurationProperties(prefix = "spring.second-datasource")
public DataSource productDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public LocalContainerEntityManagerFactoryBean productEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(productDataSource());
em.setPackagesToScan("com.baeldung.multipledb.model.product");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto", "update");
properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL8Dialect");
em.setJpaPropertyMap(properties);
return em;
}
@Bean
public PlatformTransactionManager productTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(productEntityManager().getObject());
return transactionManager;
}
}
💡 技巧:通过 @ConfigurationProperties
+ DataSourceBuilder
,Spring Boot 会自动绑定配置项到数据源实例中,省去手动 set 属性的麻烦。
7. 总结
本文通过实际示例,讲解了在 Spring Data JPA 中如何配置多个数据库,并详细说明了以下关键点:
- 实体类、Repository 按业务模块划分包路径
- 为每个数据库配置独立的
DataSource
、EntityManagerFactory
和TransactionManager
- 使用
@EnableJpaRepositories
指定扫描路径和对应 Bean - Spring Boot 中如何简化配置,使用
@ConfigurationProperties
自动绑定数据源配置
✅ 适用场景:
- 多租户系统
- 微服务架构中不同服务使用不同数据库
- 旧系统改造,需要连接多个遗留数据库
📌 完整代码示例:
可在 GitHub 项目 中查看完整实现。该项目基于 Maven 构建,可直接导入运行。