首页 > 其他分享 >SpringBoot学习---第五篇:动态数据源(多数据源自动切换)

SpringBoot学习---第五篇:动态数据源(多数据源自动切换)

时间:2023-07-28 23:04:28浏览次数:48  
标签:SpringBoot dsMap 数据源 springframework --- datasource import org

目录

一、应用场景

二、准备工作

2.1  创建数据表

2.2 添加依赖

2.3 生成 bean、dao、mapper

三、动态数据源

3.1 配置文件 application.properties

3.2 动态数据源核心代码

3.3 启动类添加注解

四、使用方法

4.1 Controller

4.2 Service

五、测试

六、Springboot2.0动态多数据源切换

一、应用场景

项目需要从自己的数据库上读取和管理数据外,还有一部分业务涉及到其他多个数据库。

为了能够灵活地指定具体的数据库,本文基于注解和AOP的方法实现多数据源自动切换。在使用过程中,只需要添加注解就可以使用,简单方便。

二、准备工作

2.1  创建数据表

1.  
2.  
CREATE TABLE `user` (
3.  
`id` int(11) NOT NULL AUTO_INCREMENT,
4.  
`name` varchar(255) NOT NULL,
5.  
`age` int(11) NOT NULL,
6.  
PRIMARY KEY (`id`)
7.  
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
8.   
9.   
10.  
USE test1;
11.  
CREATE TABLE `teacher` (
12.  
`tid` int(11) NOT NULL AUTO_INCREMENT,
13.  
`tname` varchar(255) NOT NULL,
14.  
`tage` int(11) NOT NULL,
15.  
PRIMARY KEY (`tid`)
16.  
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
17.   
18.   
19.   
20.  
USE test2;
21.  
CREATE TABLE `student` (
22.  
`sid` int(11) NOT NULL AUTO_INCREMENT,
23.  
`sname` varchar(255) NOT NULL,
24.  
`sage` int(11) NOT NULL,
25.  
PRIMARY KEY (`sid`)
26.  
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8

2.2 添加依赖

mysql:5.1.44
mybatis:1.3.2
druid:1.1.3
1.  
<?xml version="1.0" encoding="UTF-8"?>
2.  
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3.  
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.  
<modelVersion>4.0.0</modelVersion>
5.   
6.  
<groupId>com.example</groupId>
7.  
<artifactId>dynamic-data-source</artifactId>
8.  
<version>0.0.1-SNAPSHOT</version>
9.  
<packaging>jar</packaging>
10.   
11.  
<name>dynamic-data-source</name>
12.  
<description>Demo project for Spring Boot</description>
13.   
14.  
<parent>
15.  
<groupId>org.springframework.boot</groupId>
16.  
<artifactId>spring-boot-starter-parent</artifactId>
17.  
<version>1.5.8.RELEASE</version>
18.  
<relativePath/> <!-- lookup parent from repository -->
19.  
</parent>
20.   
21.  
<properties>
22.  
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
23.  
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
24.  
<java.version>1.8</java.version>
25.  
</properties>
26.   
27.  
<dependencies>
28.  
<dependency>
29.  
<groupId>org.springframework.boot</groupId>
30.  
<artifactId>spring-boot-starter-web</artifactId>
31.  
</dependency>
32.   
33.  
<!--mysql-->
34.  
<dependency>
35.  
<groupId>mysql</groupId>
36.  
<artifactId>mysql-connector-java</artifactId>
37.  
<scope>runtime</scope>
38.  
</dependency>
39.  
<!--mybatis-->
40.  
<dependency>
41.  
<groupId>org.mybatis.spring.boot</groupId>
42.  
<artifactId>mybatis-spring-boot-starter</artifactId>
43.  
<version>1.3.2</version>
44.  
</dependency>
45.  
<!--aop-->
46.  
<dependency>
47.  
<groupId>org.springframework.boot</groupId>
48.  
<artifactId>spring-boot-starter-aop</artifactId>
49.  
</dependency>
50.  
<!--数据库连接池-->
51.  
<dependency>
52.  
<groupId>com.alibaba</groupId>
53.  
<artifactId>druid</artifactId>
54.  
<version>1.1.3</version>
55.  
</dependency>
56.   
57.  
<dependency>
58.  
<groupId>org.springframework.boot</groupId>
59.  
<artifactId>spring-boot-starter-test</artifactId>
60.  
<scope>test</scope>
61.  
</dependency>
62.  
</dependencies>
63.   
64.  
<build>
65.  
<plugins>
66.  
<plugin>
67.  
<groupId>org.springframework.boot</groupId>
68.  
<artifactId>spring-boot-maven-plugin</artifactId>
69.  
</plugin>
70.  
<!-- mybatis generator 自动生成代码插件 -->
71.  
<plugin>
72.  
<groupId>org.mybatis.generator</groupId>
73.  
<artifactId>mybatis-generator-maven-plugin</artifactId>
74.  
<version>1.3.2</version>
75.  
<configuration> 
76.  
<overwrite>true</overwrite>
77.  
<verbose>true</verbose>
78.  
</configuration>
79.  
</plugin>
80.  
</plugins>
81.  
</build>
82.  
</project>

2.3 生成 bean、dao、mapper

使用MyBatis Generator自动生成,方法如下:

SpringBoot学习---第五篇:动态数据源(多数据源自动切换)_spring

三、动态数据源

3.1 配置文件 application.properties

1.  
2.  
custom.datasource.names=ds1,ds2
3.   
4.  
# 默认数据源
5.  
custom.datasource.driver-class-name=com.mysql.jdbc.Driver
6.  
custom.datasource.url=jdbc:mysql://localhost:3306/test
7.  
custom.datasource.username=root
8.  
custom.datasource.password=root
9.   
10.  
# 更多数据源
11.  
custom.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
12.  
custom.datasource.ds1.url=jdbc:mysql://localhost:3306/test1
13.  
custom.datasource.ds1.username=root
14.  
custom.datasource.ds1.password=root
15.   
16.  
custom.datasource.ds2.driver-class-name=com.mysql.jdbc.Driver
17.  
custom.datasource.ds2.url=jdbc:mysql://localhost:3306/test2
18.  
custom.datasource.ds2.username=root
19.  
custom.datasource.ds2.password=root
20.   
21.   
22.  
custom.datasource.filters=stat
23.  
custom.datasource.maxActive=100
24.  
custom.datasource.initialSize=1
25.  
custom.datasource.minIdle=1
26.  
custom.datasource.timeBetweenEvictionRunsMillis=60000
27.  
custom.datasource.minEvictableIdleTimeMillis=300000
28.  
custom.datasource.validationQuery=select 'x'
29.  
custom.datasource.testWhileIdle=true
30.  
custom.datasource.testOnBorrow=false
31.  
custom.datasource.testOnReturn=false
32.  
custom.datasource.poolPreparedStatements=true
33.  
custom.datasource.maxOpenPreparedStatements=100
34.   
35.  
mybatis.type-aliases-package=com.example.demo.model.*
36.  
mybatis.mapper-locations=classpath:mapper/**/*.xml

3.2 动态数据源核心代码

1.  
2.  
DynamicDataSourceAspect:利用AOP切面实现数据源的动态切换;
3.  
DynamicDataSourceContextHolder:动态切换数据源;
4.  
DynamicDataSourceRegister:动态数据源注册;
5.  
TargetDataSource:在方法上使用,用于指定使用哪个数据源。
6.  
package com.example.demo.datasource;
7.  
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
8.   
9.  
/**
10.  
 * 动态数据源
11.  
 */
12.  
public class DynamicDataSource extends AbstractRoutingDataSource {
13.  
@Override
14.  
protected Object determineCurrentLookupKey() {
15.  
return DynamicDataSourceContextHolder.getDataSourceType();
16.  
}
17.  
}
18.  
package com.example.demo.datasource;
19.   
20.  
import org.aspectj.lang.JoinPoint;
21.  
import org.aspectj.lang.annotation.After;
22.  
import org.aspectj.lang.annotation.Aspect;
23.  
import org.aspectj.lang.annotation.Before;
24.  
import org.slf4j.Logger;
25.  
import org.slf4j.LoggerFactory;
26.  
import org.springframework.core.annotation.Order;
27.  
import org.springframework.stereotype.Component;
28.   
29.  
/**
30.  
 * 切换数据源Advice
31.  
 */
32.  
@Aspect
33.  
@Order(-1)// 保证该AOP在@Transactional之前执行
34.  
@Component
35.  
public class DynamicDataSourceAspect {
36.  
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);
37.   
38.  
@Before("@annotation(ds)")
39.  
public void changeDataSource(JoinPoint point, TargetDataSource ds) throws Throwable {
40.  
String dsId = ds.name();
41.  
if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) {
42.  
logger.error("数据源[{}]不存在,使用默认数据源 > {}", ds.name(), point.getSignature());
43.  
}else {
44.  
logger.debug("Use DataSource : {} > {}", dsId, point.getSignature());
45.  
DynamicDataSourceContextHolder.setDataSourceType(dsId);
46.  
}
47.   
48.  
}
49.  
@After("@annotation(ds)")
50.  
public void restoreDataSource(JoinPoint point, TargetDataSource ds) {
51.  
logger.debug("Revert DataSource : {} > {}", ds.name(), point.getSignature());
52.  
DynamicDataSourceContextHolder.clearDataSourceType();
53.  
}
54.  
}
55.  
package com.example.demo.datasource;
56.   
57.  
import java.util.ArrayList;
58.  
import java.util.List;
59.   
60.  
public class DynamicDataSourceContextHolder {
61.  
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
62.  
public static List<String> dataSourceIds = new ArrayList<>();
63.   
64.  
public static void setDataSourceType(String dataSourceType) {
65.  
contextHolder.set(dataSourceType);
66.  
}
67.   
68.  
public static String getDataSourceType() {
69.  
return contextHolder.get();
70.  
}
71.   
72.  
public static void clearDataSourceType() {
73.  
contextHolder.remove();
74.  
}
75.   
76.  
/**
77.  
 * 判断指定DataSrouce当前是否存在
78.  
 */
79.  
public static boolean containsDataSource(String dataSourceId){
80.  
return dataSourceIds.contains(dataSourceId);
81.  
}
82.  
}
83.  
package com.example.demo.datasource;
84.   
85.  
import org.slf4j.Logger;
86.  
import org.slf4j.LoggerFactory;
87.  
import org.springframework.beans.MutablePropertyValues;
88.  
import org.springframework.beans.PropertyValues;
89.  
import org.springframework.beans.factory.annotation.Value;
90.  
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
91.  
import org.springframework.beans.factory.support.GenericBeanDefinition;
92.  
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
93.  
import org.springframework.boot.bind.RelaxedDataBinder;
94.  
import org.springframework.boot.bind.RelaxedPropertyResolver;
95.  
import org.springframework.context.EnvironmentAware;
96.  
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
97.  
import org.springframework.core.convert.ConversionService;
98.  
import org.springframework.core.convert.support.DefaultConversionService;
99.  
import org.springframework.core.env.Environment;
100.  
import org.springframework.core.type.AnnotationMetadata;
101.   
102.  
import javax.sql.DataSource;
103.  
import java.util.HashMap;
104.  
import java.util.Map;
105.   
106.  
/**
107.  
 * 动态数据源注册
108.  
@Import(DynamicDataSourceRegister.class)
109.  
 */
110.  
public class DynamicDataSourceRegister
111.  
implements ImportBeanDefinitionRegistrar, EnvironmentAware {
112.   
113.  
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceRegister.class);
114.  
private ConversionService conversionService = new DefaultConversionService();
115.  
private PropertyValues dataSourcePropertyValues;
116.   
117.  
// 如配置文件中未指定数据源类型,使用该默认值
118.  
private static final Object DATASOURCE_TYPE_DEFAULT = "com.alibaba.druid.pool.DruidDataSource";
119.   
120.  
// 数据源
121.  
private DataSource defaultDataSource;
122.  
private Map<String, DataSource> customDataSources = new HashMap<>();
123.   
124.  
private static String DB_NAME = "names";
125.  
private static String DB_DEFAULT_VALUE = "custom.datasource"; //配置文件中前缀
126.  
@Value("${bi.datasource.defaultname}")
127.  
private String defaultDbname;
128.   
129.  
//加载多数据源配置
130.  
@Override
131.  
public void setEnvironment(Environment env) {
132.  
initDefaultDataSource(env);
133.  
initCustomDataSources(env);
134.  
}
135.   
136.  
//初始化主数据源
137.  
private void initDefaultDataSource(Environment env) {
138.  
// 读取主数据源
139.  
RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, DB_DEFAULT_VALUE+".");
140.  
Map<String, Object> dsMap = new HashMap<>();
141.  
dsMap.put("type", propertyResolver.getProperty("type"));
142.  
dsMap.put("driver-class-name", propertyResolver.getProperty("driver-class-name"));
143.  
dsMap.put("url", propertyResolver.getProperty("url"));
144.  
dsMap.put("username", propertyResolver.getProperty("username"));
145.  
dsMap.put("password", propertyResolver.getProperty("password"));
146.   
147.  
defaultDataSource = buildDataSource(dsMap);
148.  
customDataSources.put(defaultDbname,defaultDataSource);//默认数据源放到动态数据源里
149.  
dataBinder(defaultDataSource, env);
150.  
}
151.   
152.   
153.  
//为DataSource绑定更多数据
154.  
private void dataBinder(DataSource dataSource, Environment env) {
155.  
RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);
156.  
//dataBinder.setValidator(new LocalValidatorFactory().run(this.applicationContext));
157.  
dataBinder.setConversionService(conversionService);
158.  
dataBinder.setIgnoreNestedProperties(false);//false
159.  
dataBinder.setIgnoreInvalidFields(false);//false
160.  
dataBinder.setIgnoreUnknownFields(true);//true
161.  
if (dataSourcePropertyValues == null) {
162.  
Map<String, Object> rpr = new RelaxedPropertyResolver(env, DB_DEFAULT_VALUE).getSubProperties(".");
163.  
Map<String, Object> values = new HashMap<String, Object>(rpr);
164.  
// 排除已经设置的属性
165.  
values.remove("type");
166.  
values.remove("driver-class-name");
167.  
values.remove("url");
168.  
values.remove("username");
169.  
values.remove("password");
170.  
dataSourcePropertyValues = new MutablePropertyValues(values);
171.  
}
172.  
dataBinder.bind(dataSourcePropertyValues);
173.  
}
174.   
175.  
//初始化更多数据源
176.  
private void initCustomDataSources(Environment env) {
177.  
// 读取配置文件获取更多数据源,也可以通过defaultDataSource读取数据库获取更多数据源
178.  
RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env,DB_DEFAULT_VALUE+".");
179.  
String dsPrefixs = propertyResolver.getProperty(DB_NAME);
180.  
for (String dsPrefix : dsPrefixs.split(",")) {// 多个数据源
181.  
Map<String, Object> dsMap = propertyResolver.getSubProperties(dsPrefix + ".");
182.  
DataSource ds = buildDataSource(dsMap);
183.  
customDataSources.put(dsPrefix, ds);
184.  
dataBinder(ds, env);
185.  
}
186.  
}
187.   
188.  
@Override
189.  
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
190.  
Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
191.  
// 将主数据源添加到更多数据源中
192.  
targetDataSources.put("dataSource", defaultDataSource);
193.  
DynamicDataSourceContextHolder.dataSourceIds.add("dataSource");
194.  
// 添加更多数据源
195.  
targetDataSources.putAll(customDataSources);
196.  
for (String key : customDataSources.keySet()) {
197.  
DynamicDataSourceContextHolder.dataSourceIds.add(key);
198.  
}
199.   
200.  
// 创建DynamicDataSource
201.  
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
202.  
beanDefinition.setBeanClass(DynamicDataSource.class);
203.  
beanDefinition.setSynthetic(true);
204.  
MutablePropertyValues mpv = beanDefinition.getPropertyValues();
205.  
mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
206.  
mpv.addPropertyValue("targetDataSources", targetDataSources);
207.  
registry.registerBeanDefinition("dataSource", beanDefinition);
208.   
209.  
logger.info("Dynamic DataSource Registry");
210.  
}
211.   
212.  
//创建DataSource
213.  
@SuppressWarnings("unchecked")
214.  
public DataSource buildDataSource(Map<String, Object> dsMap) {
215.  
try {
216.  
Object type = dsMap.get("type");
217.  
if (type == null)
218.  
type = DATASOURCE_TYPE_DEFAULT;// 默认DataSource
219.   
220.  
Class<? extends DataSource> dataSourceType;
221.  
dataSourceType = (Class<? extends DataSource>) Class.forName((String) type);
222.   
223.  
String driverClassName = dsMap.get("driver-class-name").toString();
224.  
String url = dsMap.get("url").toString();
225.  
String username = dsMap.get("username").toString();
226.  
String password = dsMap.get("password").toString();
227.   
228.  
DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url)
229.  
.username(username).password(password).type(dataSourceType);
230.  
return factory.build();
231.  
} catch (ClassNotFoundException e) {
232.  
e.printStackTrace();
233.  
}
234.  
return null;
235.  
}
236.  
}
237.  
package com.example.demo.datasource;
238.   
239.  
import java.lang.annotation.Documented;
240.  
import java.lang.annotation.ElementType;
241.  
import java.lang.annotation.Retention;
242.  
import java.lang.annotation.RetentionPolicy;
243.  
import java.lang.annotation.Target;
244.   
245.  
// 在方法上使用,用于指定使用哪个数据源
246.  
@Target({ ElementType.METHOD, ElementType.TYPE })
247.  
@Retention(RetentionPolicy.RUNTIME)
248.  
@Documented
249.  
public @interface TargetDataSource {
250.  
String name();
251.  
}

3.3 启动类添加注解

添加注解 @Import :注册动态多数据源;

添加 @MapperScan :将项目中对应的mapper类的路径加进来

1.  
2.   
3.  
import com.example.demo.datasource.DynamicDataSourceRegister;
4.  
import org.mybatis.spring.annotation.MapperScan;
5.  
import org.springframework.boot.SpringApplication;
6.  
import org.springframework.boot.autoconfigure.SpringBootApplication;
7.  
import org.springframework.context.annotation.Import;
8.   
9.  
@SpringBootApplication
10.  
@Import({DynamicDataSourceRegister.class}) // 注册动态多数据源
11.  
@MapperScan("com.example.demo.dao")//将项目中对应的mapper类的路径加进来就可以了
12.  
public class DynamicDataSourceApplication {
13.  
public static void main(String[] args) {
14.  
SpringApplication.run(DynamicDataSourceApplication.class, args);
15.  
}
16.  
}

四、使用方法

4.1 Controller

1.  
2.   
3.  
import com.example.demo.model.Student;
4.  
import com.example.demo.model.Teacher;
5.  
import com.example.demo.model.User;
6.  
import com.example.demo.service.DynamicDataSourceService;
7.  
import org.springframework.beans.factory.annotation.Autowired;
8.  
import org.springframework.web.bind.annotation.PathVariable;
9.  
import org.springframework.web.bind.annotation.RequestMapping;
10.  
import org.springframework.web.bind.annotation.RestController;
11.   
12.  
import java.util.List;
13.   
14.  
@RestController
15.  
@RequestMapping(value = "/dds")
16.  
public class DynamicDataSourceController {
17.   
18.  
@Autowired
19.  
private DynamicDataSourceService service;
20.   
21.  
@RequestMapping(value = "/user/{id}")
22.  
public User getAllUserData(@PathVariable Integer id){
23.  
return service.getUserData(id);
24.  
}
25.   
26.  
@RequestMapping(value = "/teacher/{id}")
27.  
public Teacher getAllTeacherData(@PathVariable Integer id) {
28.  
return service.getTeacherData(id);
29.  
}
30.   
31.  
@RequestMapping(value = "/student/{id}")
32.  
public Student getAllStudentData(@PathVariable Integer id) {
33.  
return service.getStudentData(id);
34.  
}
35.  
}

4.2 Service

注解@TargetDataSource 不能直接在接口类Mapper上使用,所以在Service上使用。

1.  
2.   
3.  
import com.example.demo.dao.StudentMapper;
4.  
import com.example.demo.dao.TeacherMapper;
5.  
import com.example.demo.dao.UserMapper;
6.  
import com.example.demo.datasource.TargetDataSource;
7.  
import com.example.demo.model.Student;
8.  
import com.example.demo.model.Teacher;
9.  
import com.example.demo.model.User;
10.  
import org.springframework.beans.factory.annotation.Autowired;
11.  
import org.springframework.stereotype.Service;
12.  
import java.util.List;
13.   
14.  
@Service
15.  
public class DynamicDataSourceService {
16.   
17.  
@Autowired
18.  
private UserMapper userMapper;
19.  
@Autowired
20.  
private TeacherMapper teacherMapper;
21.  
@Autowired
22.  
private StudentMapper studentMapper;
23.   
24.   
25.  
//不指定数据源使用默认数据源
26.  
public User getUserData(Integer id) {
27.  
return userMapper.selectByPrimaryKey(id);
28.  
}
29.   
30.  
//指定数据源-ds1
31.  
@TargetDataSource(name="ds1")
32.  
public Teacher getTeacherData(Integer id) {
33.  
return teacherMapper.selectByPrimaryKey(id);
34.  
}
35.   
36.  
//指定数据源-ds2
37.  
@TargetDataSource(name="ds2")
38.  
public Student getStudentData(Integer id) {
39.  
return studentMapper.selectByPrimaryKey(id);
40.  
}
41.  
}

五、测试

  • localhost:8080//dds/user/1

SpringBoot学习---第五篇:动态数据源(多数据源自动切换)_bc_02

  • localhost:8080//dds/teacher/1

SpringBoot学习---第五篇:动态数据源(多数据源自动切换)_bc_03

  • localhost:8080//dds/student/1

SpringBoot学习---第五篇:动态数据源(多数据源自动切换)_mysql_04

 

六、Springboot2.0动态多数据源切换

Springboot 1.x 到 Springboot 2.0配置变化有一点变化。主要是 RelaxedPropertyResolver不再可以Environment自动处理。

主要 是 DynamicDataSourceRegister 中有部分方法不一样。其他方法都不需要修改。

1.  
2.   
3.  
import org.slf4j.Logger;
4.  
import org.slf4j.LoggerFactory;
5.  
import org.springframework.beans.MutablePropertyValues;
6.  
import org.springframework.beans.factory.annotation.Value;
7.  
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
8.  
import org.springframework.beans.factory.support.GenericBeanDefinition;
9.  
import org.springframework.boot.jdbc.DataSourceBuilder;
10.  
import org.springframework.context.EnvironmentAware;
11.  
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
12.  
import org.springframework.core.env.Environment;
13.  
import org.springframework.core.type.AnnotationMetadata;
14.   
15.  
import javax.sql.DataSource;
16.  
import java.util.HashMap;
17.  
import java.util.Map;
18.   
19.  
/**
20.  
 * 动态数据源注册<br/>
21.  
@Import(DynamicDataSourceRegister.class)
22.  
 */
23.  
public class DynamicDataSourceRegister
24.  
implements ImportBeanDefinitionRegistrar, EnvironmentAware {
25.   
26.  
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceRegister.class);
27.   
28.  
// 如配置文件中未指定数据源类型,使用该默认值
29.  
private static final Object DATASOURCE_TYPE_DEFAULT = "com.alibaba.druid.pool.DruidDataSource";
30.   
31.  
// 数据源
32.  
private DataSource defaultDataSource;
33.  
private Map<String, DataSource> customDataSources = new HashMap<>();
34.   
35.  
private static String DB_NAME = "names";
36.  
private static String DB_DEFAULT_VALUE = "custom.datasource";
37.  
@Value("${bi.datasource.defaultname}")
38.  
private String defaultDbname;
39.   
40.  
/**
41.  
 * 加载多数据源配置
42.  
 */
43.  
@Override
44.  
public void setEnvironment(Environment env) {
45.  
initDefaultDataSource(env);
46.  
initCustomDataSources(env);
47.  
}
48.   
49.  
/**
50.  
 * 1.5.8 初始化主数据源
51.  
 */
52.  
// private void initDefaultDataSource(Environment env) {
53.  
// // 读取主数据源
54.  
// RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, DB_DEFAULT_VALUE+".");
55.  
// Map<String, Object> dsMap = new HashMap<>();
56.  
// dsMap.put("type", propertyResolver.getProperty("type"));
57.  
// dsMap.put("driver-class-name", propertyResolver.getProperty("driver-class-name"));
58.  
// dsMap.put("url", propertyResolver.getProperty("url"));
59.  
// dsMap.put("username", propertyResolver.getProperty("username"));
60.  
// dsMap.put("password", propertyResolver.getProperty("password"));
61.  
//
62.  
// defaultDataSource = buildDataSource(dsMap);
63.  
// customDataSources.put(defaultDbname,defaultDataSource);//默认数据源放到动态数据源里
64.  
// dataBinder(defaultDataSource, env);
65.  
// }
66.  
/**
67.  
 * 2.0.4 初始化主数据源
68.  
 */
69.  
private void initDefaultDataSource(Environment env) {
70.  
// 读取主数据源
71.  
Map<String, Object> dsMap = new HashMap<>();
72.  
dsMap.put("type", env.getProperty(DB_DEFAULT_VALUE + "." + "type"));
73.  
dsMap.put("driver-class-name", env.getProperty(DB_DEFAULT_VALUE + "." + "driver-class-name"));
74.  
dsMap.put("url", env.getProperty(DB_DEFAULT_VALUE + "." + "url"));
75.  
dsMap.put("username", env.getProperty(DB_DEFAULT_VALUE + "." + "username"));
76.  
dsMap.put("password", env.getProperty(DB_DEFAULT_VALUE + "." + "password"));
77.  
defaultDataSource = buildDataSource(dsMap);
78.  
// customDataSources.put(defaultDbname,defaultDataSource);//默认数据源放到动态数据源里
79.  
// dataBinder(defaultDataSource, env);
80.  
}
81.   
82.   
83.  
/**
84.  
 * 为DataSource绑定更多数据
85.  
 *
86.  
 */
87.  
// private void dataBinder(DataSource dataSource, Environment env) {
88.  
// RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);
89.  
// //dataBinder.setValidator(new LocalValidatorFactory().run(this.applicationContext));
90.  
// dataBinder.setConversionService(conversionService);
91.  
// dataBinder.setIgnoreNestedProperties(false);//false
92.  
// dataBinder.setIgnoreInvalidFields(false);//false
93.  
// dataBinder.setIgnoreUnknownFields(true);//true
94.  
// if (dataSourcePropertyValues == null) {
95.  
// Map<String, Object> rpr = new RelaxedPropertyResolver(env, DB_DEFAULT_VALUE).getSubProperties(".");
96.  
// Map<String, Object> values = new HashMap<String, Object>(rpr);
97.  
// // 排除已经设置的属性
98.  
// values.remove("type");
99.  
// values.remove("driver-class-name");
100.  
// values.remove("url");
101.  
// values.remove("username");
102.  
// values.remove("password");
103.  
// dataSourcePropertyValues = new MutablePropertyValues(values);
104.  
// }
105.  
// dataBinder.bind(dataSourcePropertyValues);
106.  
// }
107.   
108.  
// 初始化更多数据源
109.  
private void initCustomDataSources(Environment env) {
110.  
// 读取配置文件获取更多数据源,也可以通过defaultDataSource读取数据库获取更多数据源
111.  
// RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env,DB_DEFAULT_VALUE+".");
112.  
String dsPrefixs = env.getProperty(DB_DEFAULT_VALUE + "." + DB_NAME);
113.  
for (String dsPrefix : dsPrefixs.split(",")) {// 多个数据源
114.  
Map<String, Object> dsMap = new HashMap<>();
115.   
116.  
dsMap.put("type", env.getProperty(DB_DEFAULT_VALUE + "." + dsPrefix + ".type"));
117.  
dsMap.put("driver-class-name", env.getProperty(DB_DEFAULT_VALUE + "." + dsPrefix + ".driver-class-name"));
118.  
dsMap.put("url", env.getProperty(DB_DEFAULT_VALUE + "." + dsPrefix + ".url"));
119.  
dsMap.put("username", env.getProperty(DB_DEFAULT_VALUE + "." + dsPrefix + ".username"));
120.  
dsMap.put("password", env.getProperty(DB_DEFAULT_VALUE + "." + dsPrefix + ".password"));
121.   
122.   
123.  
DataSource ds = buildDataSource(dsMap);
124.  
customDataSources.put(dsPrefix, ds);
125.  
}
126.  
}
127.   
128.   
129.   
130.  
@Override
131.  
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
132.  
Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
133.  
// 将主数据源添加到更多数据源中
134.  
targetDataSources.put("dataSource", defaultDataSource);
135.  
DynamicDataSourceContextHolder.dataSourceIds.add("dataSource");
136.  
// 添加更多数据源
137.  
targetDataSources.putAll(customDataSources);
138.  
for (String key : customDataSources.keySet()) {
139.  
DynamicDataSourceContextHolder.dataSourceIds.add(key);
140.  
}
141.   
142.  
// 创建DynamicDataSource
143.  
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
144.  
beanDefinition.setBeanClass(DynamicDataSource.class);
145.  
beanDefinition.setSynthetic(true);
146.  
MutablePropertyValues mpv = beanDefinition.getPropertyValues();
147.  
mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
148.  
mpv.addPropertyValue("targetDataSources", targetDataSources);
149.  
registry.registerBeanDefinition("dataSource", beanDefinition);
150.   
151.  
logger.info("Dynamic DataSource Registry");
152.  
}
153.   
154.  
// 创建DataSource
155.  
@SuppressWarnings("unchecked")
156.  
public DataSource buildDataSource(Map<String, Object> dsMap) {
157.  
try {
158.  
Object type = dsMap.get("type");
159.  
if (type == null)
160.  
type = DATASOURCE_TYPE_DEFAULT;// 默认DataSource
161.   
162.  
Class<? extends DataSource> dataSourceType;
163.  
dataSourceType = (Class<? extends DataSource>) Class.forName((String) type);
164.   
165.  
String driverClassName = dsMap.get("driver-class-name").toString();
166.  
String url = dsMap.get("url").toString();
167.  
String username = dsMap.get("username").toString();
168.  
String password = dsMap.get("password").toString();
169.   
170.  
DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url)
171.  
.username(username).password(password).type(dataSourceType);
172.  
return factory.build();
173.  
} catch (ClassNotFoundException e) {
174.  
e.printStackTrace();
175.  
}
176.  
return null;
177.  
}
178.  
}

参考文献

Spring Boot 动态数据源(多数据源自动切换)


标签:SpringBoot,dsMap,数据源,springframework,---,datasource,import,org
From: https://blog.51cto.com/u_15776740/6888167

相关文章

  • Activiti6系列(5)- 核心API
    前言本来想把《疯狂工作流讲义-activiti6.0》这本书里面的实例拿过来,但是这本书我看完后,认为里面编写的activiti6的核心API代码片段不是很清晰,有不少需要雕琢的地方才好形成一篇博客。所以我就把以前看过的黑马activiti5的案例拿过来放到activiti6.0依赖中运行测试,可以正常使用,说......
  • Python-3-Python变量与运算符
    第三章:变量与运算符3-1变量的介绍1.变量只能包含字母、数字、下划线。变量名可以以字母或下划线开头,但绝不是数字开头。2.不能将python关键字作为变量名,常见python关键字有:FalseclassfinallyisreturnTruecontinueforlambdat......
  • 设计模式-桥接模式
    桥接模式(Bridge)模式定义+将抽象部分和实现接口分离,使它们可以独立变化。是一种结构性模式。UML图时序图角色定义抽象类(Abstraction)用于定义抽象类的接口,其中定义了一个Implementation实现类接口的对象,并可以维护该对象,它与Implementation具有关联关系扩充抽象......
  • Python-2-Python数据类型
    第二章:Python数据类型2-1字符串简单介绍'A'+'B'字符串连接输出'AB';'Hello'*3多次连接3次。'Hello'[0]取第一个字符;'Hello'[1]取第二个字符;'Hello'[-1]取最后一个字符;'Hello'[1:3]从e开始取取2个字符el,1+2=3所以是[1:3];'H......
  • Python-1-Python起步
    1-1选择python的理由相对于其它语言,Python容易上手,语法通俗易懂;复杂的概念已经封装好,不需要考虑内存的释放与申请,其它语言上百行代码解决的问题,Python只需要几十行甚至更短的代码。Python设计哲学:优雅、明确、简单//打印print("HelloWorld");//交换xyx,y=y,x 1-2python......
  • [代码随想录]Day03-链表part01
    题目:203.移除链表元素思路:做链表首先最好有个虚拟头指针,这样做删除添加操作的时候可以统一操作否则还得特判。那删除元素的过程,从虚拟头指针开始要做以下几步:如果当前p的next不为空,那么就可以进行判断如果p的next的值是val(↑p的next不为空↑),那么就把p的next修改为p的下......
  • 文心一言 VS 讯飞星火 VS chatgpt (67)-- 算法导论6.5 6题
    文心一言VS讯飞星火VSchatgpt(67)--算法导论6.56题六、在HEAP-INCREASE-KEY的第5行的交换操作中,一般需要通过三次赋值来完成。想一想如何利用INSERTION-SORT内循环部分的思想,只用一次赋值就完成这一交换操作?文心一言:在HEAP-INCREASE-KEY的第5行交换操作中,我们可以通......
  • JavaScript学习 -- HMAC算法基本原理
    HMAC(Hash-basedMessageAuthenticationCode)算法是一种基于哈希算法的消息认证码算法。它可以用于验证和保护数据在传输过程中的完整性和真实性。在JavaScript中,我们可以使用HMAC算法来保证数据的安全性。本篇文章将介绍HMAC算法的基本原理和相关技术,并提供一些实例来演示如何在Ja......
  • t113-c-framebuffer-画点篇
    本来今天是要结合udp的例子传输数据的,但是被一个数学逻辑搞了半天,记录一下避坑吧。代码#include<stdio.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include"linux/fb.h"#include<sys/ioctl.h>#include<sys/mman.h>#include<......
  • 《摆与混》第二十四章--7月28日--周五
    明天就是周末·!!!!1.今天做了什么:今天8点半起床。洗漱后,简单吃了早饭,早上小学了一下,下午邀请哥们来家里一起读书,随便晚上一起吃了个饭,饭后散了个步,经典PTA休息一天。2.解决了什么问题:Java课程推进,复习了一下。3.明天干什么:预计继续学习Java,PTA同步跟进;PS:不想学习,我想成为螺丝刀;......