0
点赞
收藏
分享

微信扫一扫

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

目录

一、应用场景

二、准备工作

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学习---第五篇:动态数据源(多数据源自动切换)_mysql_02

  • localhost:8080//dds/teacher/1

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

  • localhost:8080//dds/student/1

SpringBoot学习---第五篇:动态数据源(多数据源自动切换)_bc_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 动态数据源(多数据源自动切换)


举报

相关推荐

0 条评论