Spring Boot多数据源及动态数据源

标签: 无 分类: 未分类 创建时间:2020-04-15 05:26:01 更新时间:2024-11-15 10:49:43

项目的需求描述:我有多个数据库,每一个数据库分属于不同的厂家,但是数据库结构是相同的,我要通过切换不同的数据库,使应用服务器连接不通的数据库,来展示不同的数据。假如说,每一个数据库搭配一套一模一样的应用程序和服务器,这当然可以满足需求,但是我只有一台服务器,一台服务器上有多个数据库,我总不能再tomcat中部署多套应用程序,然后根据不同的url,使用不同的应用程序进行服务吧,感觉有点奇怪。

参考文章中,有对处理多个数据库的读写的策略进行了描述:

(1) 多套数据源:即针对一个数据库建立一套数据处理逻辑,每套数据库都包括数据源配置、会话工厂( sessionFactory )、连接、SQL 操作、实体。各套数据库相互独立。
(2) 动态数据源:确定数量的多个数据源共用一个会话工厂,根据条件动态选取数据源进行连接、SQL 操作。
(3) 参数化变更数据源:根据参数添加数据源,并进行数据源切换,数据源数量不确定。通常用于对多个数据库的管理工作。

应用场景:

(1) 数据库高性能场景:主从,包括一主一从,一主多从等,在主库进行增删改操作,在从库进行读操作。
(2) 数据库高可用场景:主备,包括一往一备,多主多备等,在数据库无法访问时可以切换。
(3) 同构或异构数据的业务处理:需要处理的数据存储在不同的数据库中,包括同构(如都是 MySQL ),异构(如一个MySQL ,另外是 PG 或者 Oracle )。

下面以配置sqlserver和postgresql为例子,结合参考文章1,并进行部分修改,讲解如何配置多个数据源

1.添加依赖

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
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<dependencies>
<!--添加jpa支持-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<!--postgresql-->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>

<!--sql server -->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>sqljdbc4</artifactId>
<version>4.0</version>
</dependency>
</dependencies>

2.编辑application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
spring:
datasource:
first:
driver-class-name: org.postgresql.Driver
jdbc-url: jdbc:postgresql://localhost:5432/hdxs
username: postgres
password: 1q2w3e4r
connection-timeout: 20000
maximum-pool-size: 5
second:
driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
jdbc-url: jdbc:sqlserver://xxx:xxx;DatabaseName=xxx
username: xxx
password: xxx
connection-timeout: 20000
maximum-pool-size: 5
jpa:
hibernate:
naming:
implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
show-sql: true

3.编写实体和Repository

我这里有两个数据源,就分别对每一个数据源编写相应的实体类以及Repository,这里我就不贴源码了,贴个目录结构吧,因为包名,在以后还有用的着的地方

4.编写DataSourceConfiguration

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
@Configuration
public class DataSourceConfiguration {

/**
* 第一个数据连接,默认优先级最高
* @return
*/
@Bean(name = "dataSourceFirst")
@Primary
@ConfigurationProperties(prefix = "spring.datasource.first")
public DataSource dataSourceFirst() {
//这种方式的配置默认只满足spring的配置方式,如果使用其他数据连接(druid),需要自己独立获取配置
return DataSourceBuilder.create().build();
}

/**
* 第二个数据源
* @return
*/
@Bean(name = "dataSourceSecond")
@ConfigurationProperties(prefix = "spring.datasource.second")
public DataSource dataSourceSecond() {
return DataSourceBuilder.create().build();
}
}

5.编写第一个数据源配置

这里的basePackages,和packages等,要和你自己的entity以及reposities包的路径相同。

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
@Configuration
@EntityScan(basePackages = "com.proheng.gis.postgresqlEntity")
//1、实体扫描
//2、实体管理ref
//3、事务管理
@EnableJpaRepositories(
basePackages = "com.proheng.gis.postgresqlReposities",
entityManagerFactoryRef = "firstEntityManagerFactoryBean",
transactionManagerRef = "firstTransactionManager")
@EnableTransactionManagement
public class JpaFirstConfiguration {

//第一个数据源,可以不加Qualifier
@Autowired
@Qualifier("dataSourceFirst")
private DataSource dataSource;

//jpa其他参数配置
@Autowired
private JpaProperties jpaProperties;

//实体管理工厂builder
@Autowired
private EntityManagerFactoryBuilder factoryBuilder;

/**
* 配置第一个实体管理工厂的bean
* @return
*/
@Bean(name = "firstEntityManagerFactoryBean")
@Primary
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {
return factoryBuilder.dataSource(dataSource)
//这一行的目的是加入jpa的其他配置参数比如(ddl-auto: update等)
//当然这个参数配置可以在事务配置的时候也可以
.properties(getVendorProperties(dataSource))
.packages("com.proheng.gis.postgresqlEntity")
.persistenceUnit("firstPersistenceUnit")
.build();
}

@Autowired
private HibernateProperties hibernateProperties;

private Map<String, Object> getVendorProperties(DataSource dataSource) {
return hibernateProperties.determineHibernateProperties(
jpaProperties.getProperties(), new HibernateSettings());
}

/**
* EntityManager不过解释,用过jpa的应该都了解
* @return
*/
@Bean(name = "firstEntityManager")
@Primary
public EntityManager entityManager() {
return entityManagerFactoryBean().getObject().createEntityManager();
}

/**
* jpa事务管理
* @return
*/
@Bean(name = "firstTransactionManager")
@Primary
public JpaTransactionManager transactionManager() {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setEntityManagerFactory(entityManagerFactoryBean().getObject());
return jpaTransactionManager;
}
}

6.编写第二个数据源配置

方法同上,也要主要相应的包名

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
57
58
59
60
61
62
63
64
65
@Configuration
@EntityScan(basePackages = "com.proheng.gis.sqlserverEntity")
//1、实体扫描
//2、实体管理ref
//3、事务管理
@EnableJpaRepositories(
basePackages = "com.proheng.gis.sqlserverReposities",
entityManagerFactoryRef = "secondEntityManagerFactoryBean",
transactionManagerRef = "secondTransactionManager")
@EnableTransactionManagement
public class JpaSecondConfiguration {

//第二个数据源,必须加Qualifier
@Autowired
@Qualifier("dataSourceSecond")
private DataSource dataSource;

//jpa其他参数配置
@Autowired
private JpaProperties jpaProperties;

//实体管理工厂builder
@Autowired
private EntityManagerFactoryBuilder factoryBuilder;

/**
* 配置第二个实体管理工厂的bean
* @return
*/
@Bean(name = "secondEntityManagerFactoryBean")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {
return factoryBuilder.dataSource(dataSource)
.properties(getVendorProperties(dataSource))
.packages("com.proheng.gis.sqlserverEntity")
.persistenceUnit("secondPersistenceUnit")
.build();
}
@Autowired
private HibernateProperties hibernateProperties;

private Map<String, Object> getVendorProperties(DataSource dataSource) {
return hibernateProperties.determineHibernateProperties(
jpaProperties.getProperties(), new HibernateSettings());
}
/**
* EntityManager不过解释,用过jpa的应该都了解
* @return
*/
@Bean(name = "secondEntityManager")
public EntityManager entityManager() {
return entityManagerFactoryBean().getObject().createEntityManager();
}

/**
* jpa事务管理
* @return
*/
@Bean(name = "secondTransactionManager")
public JpaTransactionManager transactionManager() {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setEntityManagerFactory(entityManagerFactoryBean().getObject());
return jpaTransactionManager;
}
}

其中getVendorProperties方法,可以通过下面的形式,为每一个数据源配置不同的属性,比如方言:

1
2
3
4
5
6
7
private Map<String, Object> getVendorProperties(DataSource dataSource) {
Map<String,String> properties=new HashMap<>();
properties.put("hibernate.temp.use_jdbc_metadata_defaults","false");
properties.put("hibernate.dialect","org.hibernate.dialect.SQLServer2012Dialect");
return hibernateProperties.determineHibernateProperties(
properties, new HibernateSettings());
}

这样就配置了两个不同的数据源了,我在想,其实是不是还可以配置三个,四个数据源,只要有数据源配置类就好了。

参考文章:
1.springboot之jpa多数据源 (根据这篇文章去修改的应用程序,但是有个问题就是关于jpaProperties.getHibernateProperties,新版里已经没有了,于是找了另一个方法,参考文章2)
2.已解决:jpaProperties.getHibernateProperties(new HibernateSettings());报错,Spring Boot 配置多数据源
3.SpringBoot JPA 多数据源的使用
4.Spring DATA JPA 配置多数据源
5.【spring boot】12.spring boot对多种不同类型数据库,多数据源配置使用 (这里提到了命名策略和DDL策略)
小额赞助
本人提供免费与付费咨询服务,感谢您的支持!赞助请发邮件通知,方便公布您的善意!
**光 3.01 元
Sun 3.00 元
bibichuan 3.00 元
微信公众号
广告位
诚心邀请广大金主爸爸洽谈合作
每日一省
isNaN 和 Number.isNaN 函数的区别?

1.函数 isNaN 接收参数后,会尝试将这个参数转换为数值,任何不能被转换为数值的的值都会返回 true,因此非数字值传入也会返回 true ,会影响 NaN 的判断。

2.函数 Number.isNaN 会首先判断传入参数是否为数字,如果是数字再继续判断是否为 NaN ,不会进行数据类型的转换,这种方法对于 NaN 的判断更为准确。

每日二省
为什么0.1+0.2 ! == 0.3,如何让其相等?

一个直接的解决方法就是设置一个误差范围,通常称为“机器精度”。对JavaScript来说,这个值通常为2-52,在ES6中,提供了Number.EPSILON属性,而它的值就是2-52,只要判断0.1+0.2-0.3是否小于Number.EPSILON,如果小于,就可以判断为0.1+0.2 ===0.3。

每日三省
== 操作符的强制类型转换规则?

1.首先会判断两者类型是否**相同,**相同的话就比较两者的大小。

2.类型不相同的话,就会进行类型转换。

3.会先判断是否在对比 null 和 undefined,是的话就会返回 true。

4.判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number。

5.判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断。

6.判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断。

每日英语
Happiness is time precipitation, smile is the lonely sad.
幸福是年华的沉淀,微笑是寂寞的悲伤。