Spring Boot Jpa多数据源配置
前言
随着业务量发展,我们通常会进行数据库拆分或是引入其他数据库,从而我们需要配置多个数据源,如:user一个库,business一个库。那么接下来我们就要考虑怎么去在spring boot中实现多个数据源的配置。
源码下载
实现
建表
首先是建表语句,我们要建立两个数据库,并各库内新建一张表
user表
mysql> use `user`;
mysql> select * from `user`;
+----+-------+----------+
| id | name | password |
+----+-------+----------+
| 1 | 用户A | ****** |
+----+-------+----------+
1 row in set
business表
mysql> use `business`;
mysql> select * from `business`;
+----+-------+-------------+
| id | name | description |
+----+-------+-------------+
| 1 | 业务A | 业务A描述 |
+----+-------+-------------+
1 row in set
接下来我们通过代码实现对两个库内的多张表进行查询。
配置
首先,创建一个Spring配置类,定义两个DataSource用来读取application.yml中的不同配置。本文中,我们user做为主数据源,主数据源配置为spring.datasource.user开头的配置,business数据源配置为spring.datasource.business开头的配置。
@Configuration
public class DataSourceConfig {
@Primary
@Bean(name = "userDataSource")
@Qualifier("userDataSource")
@ConfigurationProperties(prefix = "spring.datasource.user")
public DataSource userDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "businessDataSource")
@Qualifier("businessDataSource")
@ConfigurationProperties(prefix = "spring.datasource.business")
public DataSource businessDataSource() {
return DataSourceBuilder.create().build();
}
}
对应的配置文件application.yml如下:
spring:
datasource:
user:
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/user
username: root
password: 123456
business:
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/business
username: root
password: 123456
接下来我们对各数据源进行jpa的配置
主数据源User
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactoryUser",
transactionManagerRef = "transactionManagerUser",
//设置Repository所在位置
basePackages = {"com.ppc.spring.example.jpamultidatasource.repository.user"})
public class UserConfig {
@Autowired
@Qualifier("userDataSource")
private DataSource userDataSource;
@Autowired
private JpaProperties jpaProperties;
@Autowired
private HibernateProperties hibernateProperties;
@Primary
@Bean(name = "entityManagerUser")
public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
return entityManagerFactoryUser(builder).getObject().createEntityManager();
}
@Primary
@Bean(name = "entityManagerFactoryUser")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryUser(EntityManagerFactoryBuilder builder) {
return builder
.dataSource(userDataSource)
//设置entity所在位置
.packages("com.ppc.spring.example.jpamultidatasource.entity.user")
.persistenceUnit("userPersistenceUnit")
.properties(getVendorProperties())
.build();
}
private Map<String, Object> getVendorProperties() {
return hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(), new HibernateSettings());
}
@Primary
@Bean(name = "transactionManagerUser")
public PlatformTransactionManager transactionManagerUser(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(entityManagerFactoryUser(builder).getObject());
}
}
其他数据源business
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactoryBusiness",
transactionManagerRef = "transactionManagerBusiness",
//设置repository所在位置
basePackages = {"com.ppc.spring.example.jpamultidatasource.repository.business"})
public class BusinessConfig {
@Autowired
@Qualifier("businessDataSource")
private DataSource businessDataSource;
@Autowired
private JpaProperties jpaProperties;
@Autowired
private HibernateProperties hibernateProperties;
@Bean(name = "entityManagerBusiness")
public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
return entityManagerFactoryBusiness(builder).getObject().createEntityManager();
}
@Bean(name = "entityManagerFactoryBusiness")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBusiness(EntityManagerFactoryBuilder builder) {
return builder
.dataSource(businessDataSource)
.properties(getVendorProperties())
//设置实体类所在位置
.packages("com.ppc.spring.example.jpamultidatasource.entity.business")
.persistenceUnit("businessPersistenceUnit")
.build();
}
private Map<String, Object> getVendorProperties() {
return hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(), new HibernateSettings());
}
@Bean(name = "transactionManagerBusiness")
PlatformTransactionManager transactionManagerBusiness(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(entityManagerFactoryBusiness(builder).getObject());
}
}
配置中需要注意以下几点:
Spring Boot 1.5.x
private Map<String, String> getVendorProperties() {
return jpaProperties.getHibernateProperties(userDataSource);
}
Spring Boot 2.0.x
private Map<String, Object> getVendorProperties() {
return jpaProperties.getHibernateProperties(new HibernateSettings());
}
Spring Boot 2.1.0参见上文代码,引进了HibernateProperties。同时,在Spring Boot 2.1.0中默认的mysql-connector-java版本为8.0.13,连接低版本mysql配置上比较繁琐,建议在配置文件中手动指定相应版本,如本文中指定5.1.46这个版本。
runtimeOnly('mysql:mysql-connector-java:5.1.46')
repository、entity的所在位置,要和实际保存的位置一致。
主数据源的一些配置需要添加@Primary作为spring默认的首选项,其他数据源无需添加该注解。
通过查看相关源码我们看到Spring Boot中JpaProperties的代码一直在调整,这里我们将properties相关代码单独提取出作为一个单独的方法getVendorProperties展示版本间的区别。其中:
查询
完成了所有的配置,接下来我们就可以开始写个简单代码验证我们配置了
@RestController
@SpringBootApplication
public class JpaMultiDatasourceApplication {
@Autowired
UserRepository userRepository;
@Autowired
BusinessRepository businessRepository;
public static void main(String[] args) {
SpringApplication.run(JpaMultiDatasourceApplication.class, args);
}
@GetMapping("/user/{id}")
public User getUser(@PathVariable Long id) {
return userRepository.findById(id).orElse(null);
}
@GetMapping("/business/{id}")
public Business getBusiness(@PathVariable Long id) {
return businessRepository.findById(id).orElse(null);
}
}
我们对外暴露了两个接口,分别访问user表、business表确认可以正常获取数据。查询结果如下:
请求:http://localhost:8080/user/1
结果:{"id":1,"name":"用户A","password":"******"}
请求:http://localhost:8080/business/1
结果:{"id":1,"name":"业务A","description":"业务A描述"}
就此,我们双数据源的配置和验证工作就完成了。