之前在介绍使用Spring-data-jpa时,都使用了单数据源。在单数据源的情况下,Spring Boot的配置非常简单,只需要在application.properties
文件中配置连接参数即可。但是往往随着业务量发展,我们通常会进行数据库拆分或是引入其他数据库,从而我们需要配置多个数据源,下面基于Spring-data-jpa的例子介绍多数据源的配置方式。
springboot1.x jpa 配置多数据源(多数据源:大于等于2个数据库)
一、项目结构图(总体浏览一下最终完成后的建包样式)
具体展开图(总体浏览一下最终完成后的建包样式):
二、创建一个Spring配置类,定义两个DataSource用来读取application.properties中
的不同配置。如下例子中,主数据源配置为spring.datasource.primary
开头的配置,第二数据源配置为spring.datasource.secondary
开头的配置。
【DataSourceConfig.java】
1 package com.didispace.config;
2
3 import org.springframework.beans.factory.annotation.Qualifier;
4 import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
5 import org.springframework.boot.context.properties.ConfigurationProperties;
6 import org.springframework.context.annotation.Bean;
7 import org.springframework.context.annotation.Configuration;
8 import org.springframework.context.annotation.Primary;
9
10 import javax.sql.DataSource;
11
12 @Configuration
13 public class DataSourceConfig {
14
15 @Bean(name = "primaryDataSource")
16 @Qualifier("primaryDataSource")
17 @Primary
18 @ConfigurationProperties(prefix="spring.datasource.primary")
19 public DataSource primaryDataSource() {
20 return DataSourceBuilder.create().build();
21 }
22
23 @Bean(name = "secondaryDataSource")
24 @Qualifier("secondaryDataSource")
25 @ConfigurationProperties(prefix="spring.datasource.secondary")
26 public DataSource secondaryDataSource() {
27 return DataSourceBuilder.create().build();
28 }
29
30 }
对应的application.properties
配置如下:
1 spring.datasource.primary.url=jdbc:mysql://localhost:3306/scott
2 spring.datasource.primary.username=root
3 spring.datasource.primary.password=root
4 spring.datasource.primary.driver-class-name=com.mysql.jdbc.Driver
5
6 spring.datasource.secondary.url=jdbc:mysql://localhost:3306/scott_2
7 spring.datasource.secondary.username=root
8 spring.datasource.secondary.password=root
9 spring.datasource.secondary.driver-class-name=com.mysql.jdbc.Driver
10
11 #display sql in console,设置将每一条jpa指令都打印在控制台中
12 spring.jpa.show-sql=true
13
14 spring.main.allow-bean-definition-overriding=true
三、数据源的JPA配置(几个数据源,几个相应名称匹配的配置,自己理解,后期尽量改成批量方式)
【PrimaryConfig.java】
1 package com.didispace.config;
2
3 import org.springframework.beans.factory.annotation.Autowired;
4 import org.springframework.beans.factory.annotation.Qualifier;
5 import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
6 import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
7 import org.springframework.context.annotation.Bean;
8 import org.springframework.context.annotation.Configuration;
9 import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
10 import org.springframework.orm.jpa.JpaTransactionManager;
11 import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
12 import org.springframework.transaction.PlatformTransactionManager;
13 import org.springframework.transaction.annotation.EnableTransactionManagement;
14
15 import javax.persistence.EntityManager;
16 import javax.sql.DataSource;
17 import java.util.Map;
18
19 @Configuration
20 @EnableTransactionManagement
21 @EnableJpaRepositories(
22 entityManagerFactoryRef = "entityManagerFactoryPrimary",
23 transactionManagerRef = "transactionManagerPrimary",
24 basePackages = {"com.didispace.repository.primary"}) //设置Repository所在位置
25 public class PrimaryConfig {
26
27 @Autowired
28 @Qualifier("primaryDataSource")
29 private DataSource primaryDataSource;
30
31 @Bean(name = "entityManagerPrimary")
32 public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
33 return entityManagerFactoryPrimary(builder).getObject().createEntityManager();
34 }
35
36 @Bean(name = "entityManagerFactoryPrimary")
37 public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary(EntityManagerFactoryBuilder builder) {
38 return builder
39 .dataSource(primaryDataSource)
40 .properties(getVendorProperties(primaryDataSource))
41 .packages("com.didispace.entity.primary") //设置实体类所在位置
42 .persistenceUnit("primaryPersistenceUnit")
43 .build();
44 }
45
46 @Autowired
47 private JpaProperties jpaProperties;
48
49 private Map<String, String> getVendorProperties(DataSource dataSource) {
50 return jpaProperties.getHibernateProperties(dataSource);
51 }
52
53 @Bean(name = "transactionManagerPrimary")
54 public PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) {
55 return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject());
56 }
57
58 }
【SecondaryConfig.java】
1 package com.didispace.config;
2
3 import org.springframework.beans.factory.annotation.Autowired;
4 import org.springframework.beans.factory.annotation.Qualifier;
5 import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
6 import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
7 import org.springframework.context.annotation.Bean;
8 import org.springframework.context.annotation.Configuration;
9 import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
10 import org.springframework.orm.jpa.JpaTransactionManager;
11 import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
12 import org.springframework.transaction.PlatformTransactionManager;
13 import org.springframework.transaction.annotation.EnableTransactionManagement;
14
15 import javax.persistence.EntityManager;
16 import javax.sql.DataSource;
17 import java.util.Map;
18
19 @Configuration
20 @EnableTransactionManagement
21 @EnableJpaRepositories(
22 entityManagerFactoryRef = "entityManagerFactorySecondary",
23 transactionManagerRef = "transactionManagerSecondary",
24 basePackages = {"com.didispace.repository.secondary"}) //设置Repository所在位置
25 public class SecondaryConfig {
26
27 @Autowired
28 @Qualifier("secondaryDataSource")
29 private DataSource secondaryDataSource;
30
31 @Bean(name = "entityManagerSecondary")
32 public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
33 return entityManagerFactorySecondary(builder).getObject().createEntityManager();
34 }
35
36 @Bean(name = "entityManagerFactorySecondary")
37 public LocalContainerEntityManagerFactoryBean entityManagerFactorySecondary(EntityManagerFactoryBuilder builder) {
38 return builder
39 .dataSource(secondaryDataSource)
40 .properties(getVendorProperties(secondaryDataSource))
41 .packages("com.didispace.entity.secondary") //设置实体类所在位置
42 .persistenceUnit("secondaryPersistenceUnit")
43 .build();
44 }
45
46 @Autowired
47 private JpaProperties jpaProperties;
48
49 private Map<String, String> getVendorProperties(DataSource dataSource) {
50 return jpaProperties.getHibernateProperties(dataSource);
51 }
52
53 @Bean(name = "transactionManagerSecondary")
54 PlatformTransactionManager transactionManagerSecondary(EntityManagerFactoryBuilder builder) {
55 return new JpaTransactionManager(entityManagerFactorySecondary(builder).getObject());
56 }
57
58 }
四、最后,分别在这两个包下创建各自的实体和数据访问接口
测试如下:
编写测试代码:
1 package com.didispace;
2
3 import com.didispace.entity.secondary.Salgrade;
4 import com.didispace.repository.secondary.SalgradeRepository;
5 import com.didispace.entity.primary.Dept;
6 import com.didispace.repository.primary.DeptRepository;
7 import org.junit.Before;
8 import org.junit.Test;
9 import org.junit.runner.RunWith;
10 import org.springframework.beans.factory.annotation.Autowired;
11 import org.springframework.boot.test.SpringApplicationConfiguration;
12 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
13
14 import java.util.Iterator;
15 import java.util.List;
16
17
18 @RunWith(SpringJUnit4ClassRunner.class)
19 @SpringApplicationConfiguration(Application.class)
20 public class ApplicationTests {
21
22 @Autowired
23 private DeptRepository deptRepository;
24 @Autowired
25 private SalgradeRepository salgradeRepository;
26
27 @Before
28 public void setUp() {
29 }
30
31 @Test
32 public void test() throws Exception {
33
34
35 System.out.println("--- --- --- 结果如下 --- --- ---");
36 System.out.println("【数据库scott】");
37 List<Dept> ans = deptRepository.findByDname("FILL");
38 Iterator<Dept> it = ans.iterator();
39 while(it.hasNext()){
40 Dept d = it.next();
41 System.out.println(d);
42
43 }
44
45 System.out.println("--- --- --- --- --- --- --- --- ");
46 System.out.println("【数据库scott_2】");
47 Salgrade grade = salgradeRepository.findByGrade(1);
48 System.out.println(grade);
49 }
50
51
52 }
涉及的数据库表:
输出结果:
springboot2.x jpa 配置多数据源(多数据源:大于等于2个数据库)
随着Springboot升级到2.0,原来1.5.x的Jpa多数据源配置不能用了。现在总结一下Springboot2.0的jpa多数据源配置。
一、项目结构图(总体浏览一下最终完成后的建包样式)
二、创建一个Spring配置类,定义两个DataSource用来读取application.properties中
的不同配置。如下例子中,主数据源配置为spring.datasource.primary
开头的配置,第二数据源配置为spring.datasource.secondary
开头的配置。
【DataSourceConfig.java】
1 package com.angei.day0924jpa.Config;
2
3 import org.springframework.beans.factory.annotation.Qualifier;
4 import org.springframework.boot.context.properties.ConfigurationProperties;
5 import org.springframework.boot.jdbc.DataSourceBuilder;
6 import org.springframework.context.annotation.Bean;
7 import org.springframework.context.annotation.Configuration;
8 import org.springframework.context.annotation.Primary;
9
10 import javax.sql.DataSource;
11
12
13 @Configuration
14 public class DataSourceConfig {
15
16 @Bean(name = "primaryDataSource")
17 @Qualifier("primaryDataSource")
18 @Primary
19 @ConfigurationProperties(prefix="spring.datasource.primary")
20 public DataSource primaryDataSource() {
21 return DataSourceBuilder.create().build();
22 }
23
24 @Bean(name = "secondaryDataSource")
25 @Qualifier("secondaryDataSource")
26 @ConfigurationProperties(prefix="spring.datasource.secondary")
27 public DataSource secondaryDataSource() {
28 return DataSourceBuilder.create().build();
29 }
30
31 }
对应的application.properties
配置如下:
1 # 多数据源配置
2 #primary
3 spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/scott?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf-8
4 spring.datasource.primary.username=root
5 spring.datasource.primary.password=root
6 spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver
7
8 #secondary
9 spring.datasource.secondary.jdbc-url=jdbc:mysql://localhost:3306/scott_2?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf-8
10 spring.datasource.secondary.username=root
11 spring.datasource.secondary.password=root
12 spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver
13
14
15 #display sql in console,设置将每一条jpa指令都打印在控制台中
16 spring.jpa.show-sql=true
17
18 spring.main.allow-bean-definition-overriding=true
注意:
关于异常提示:
dataSource或dataSourceClassName或jdbcUrl是必需的!
解决方案:
将:
spring.datasource.primary.url=jdbc:mysql://IP地址/dbName
修改为:
spring.datasource.primary.jdbc-url=jdbc:mysql://IP地址/dbName
三、数据源的JPA配置(几个数据源,几个相应名称匹配的配置,自己理解,后期尽量改成批量方式)
【PrimaryConfig.java】
1 package com.angei.day0924jpa.Config;
2
3 import org.springframework.beans.factory.annotation.Qualifier;
4 import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
5 import org.springframework.context.annotation.Bean;
6 import org.springframework.context.annotation.Configuration;
7 import org.springframework.context.annotation.Primary;
8 import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
9 import org.springframework.orm.jpa.JpaTransactionManager;
10 import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
11 import org.springframework.transaction.PlatformTransactionManager;
12 import org.springframework.transaction.annotation.EnableTransactionManagement;
13
14 import javax.annotation.Resource;
15 import javax.persistence.EntityManager;
16 import javax.sql.DataSource;
17 import java.util.Properties;
18
19
20 @Configuration
21 @EnableTransactionManagement
22 @EnableJpaRepositories(
23 entityManagerFactoryRef = "entityManagerFactoryPrimary",
24 transactionManagerRef = "transactionManagerPrimary",
25 basePackages = {"com.angei.day0924jpa.repository.primary"}) //设置Repository所在位置
26
27 public class PrimaryConfig {
28
29 @Resource
30 @Qualifier("primaryDataSource")
31 private DataSource primaryDataSource;
32
33 @Primary
34 @Bean(name = "entityManagerPrimary")
35 public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
36 return entityManagerFactoryPrimary(builder).getObject().createEntityManager();
37 }
38
39 @Resource
40 private Properties jpaProperties;
41
42
43 @Primary
44 @Bean(name = "entityManagerFactoryPrimary")
45 public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary(EntityManagerFactoryBuilder builder) {
46 LocalContainerEntityManagerFactoryBean entityManagerFactory = builder
47 .dataSource(primaryDataSource)
48 .packages("com.angei.day0924jpa.entity.primary") //设置实体类所在位置
49 .persistenceUnit("primaryPersistenceUnit")
50 .build();
51 entityManagerFactory.setJpaProperties(jpaProperties);
52 return entityManagerFactory;
53 }
54
55 @Primary
56 @Bean(name = "transactionManagerPrimary")
57 public PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) {
58 return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject());
59 }
60
61 }
【SecondaryConfig.java】
1 package com.angei.day0924jpa.Config;
2
3 import org.springframework.beans.factory.annotation.Qualifier;
4 import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
5 import org.springframework.context.annotation.Bean;
6 import org.springframework.context.annotation.Configuration;
7 import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
8 import org.springframework.orm.jpa.JpaTransactionManager;
9 import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
10 import org.springframework.transaction.PlatformTransactionManager;
11 import org.springframework.transaction.annotation.EnableTransactionManagement;
12
13 import javax.annotation.Resource;
14 import javax.persistence.EntityManager;
15 import javax.sql.DataSource;
16 import java.util.Properties;
17
18
19 @Configuration
20 @EnableTransactionManagement
21 @EnableJpaRepositories(
22 entityManagerFactoryRef = "entityManagerFactorySecondary",
23 transactionManagerRef = "transactionManagerSecondary",
24 basePackages = {"com.angei.day0924jpa.repository.secondary"}) //设置Repository所在位置
25 public class SecondaryConfig {
26
27 @Resource
28 @Qualifier("secondaryDataSource")
29 private DataSource secondaryDataSource;
30
31 @Resource
32 private Properties jpaProperties;
33
34
35 @Bean(name = "entityManagerSecondary")
36 public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
37 return entityManagerFactorySecondary(builder).getObject().createEntityManager();
38 }
39
40 @Bean(name = "entityManagerFactorySecondary")
41 public LocalContainerEntityManagerFactoryBean entityManagerFactorySecondary(EntityManagerFactoryBuilder builder) {
42 LocalContainerEntityManagerFactoryBean entityManagerFactory
43 = builder
44 .dataSource(secondaryDataSource)
45 .packages("com.angei.day0924jpa.entity.secondary") //设置实体类所在位置
46 .persistenceUnit("secondaryPersistenceUnit")//持久化单元创建一个默认即可,多个便要分别命名
47 .build();
48 entityManagerFactory.setJpaProperties(jpaProperties);
49 return entityManagerFactory;
50 }
51
52 @Bean(name = "transactionManagerSecondary")
53 public PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) {
54 return new JpaTransactionManager(entityManagerFactorySecondary(builder).getObject());
55 }
56 }
说明:从SpringBoot1.x 升级至SpringBoot2.1.x 上述配置做了如下的修改:
四、最后,分别在这两个包下创建各自的实体和数据访问接口
测试如下:
编写测试代码:
【ContractController.java】
1 package com.angei.day0924jpa.controller;
2
3 import com.angei.day0924jpa.entity.primary.Dept;
4 import com.angei.day0924jpa.entity.secondary.Salgrade;
5 import com.angei.day0924jpa.repository.primary.DeptRepository;
6 import com.angei.day0924jpa.repository.secondary.SalgradeRepository;
7 import org.springframework.beans.factory.annotation.Autowired;
8 import org.springframework.stereotype.Controller;
9 import org.springframework.web.bind.annotation.RequestMapping;
10 import org.springframework.web.bind.annotation.ResponseBody;
11
12 import java.util.Iterator;
13 import java.util.List;
14
15 @Controller
16 @RequestMapping("/contract")
17 public class ContractController {
18
19 @Autowired
20 private DeptRepository deptRepository;
21
22 @Autowired
23 private SalgradeRepository salgradeRepository;
24
25 @RequestMapping("/testing")
26 @ResponseBody
27 public void ContractQuery(){
28 System.out.println("【数据库1】");
29 List<Dept> ans = deptRepository.findByDname("FILL");
30 Iterator<Dept> it = ans.iterator();
31 while(it.hasNext()){
32
33 Dept d = it.next();
34 System.out.println(d);
35 }
36 System.out.println("【数据库2】");
37 Salgrade res = salgradeRepository.findByGrade(2);
38 System.out.println(res);
39 }
40 }
访问:http://localhost:8080/contract/testing
显示如下: