springboot多数据源
本文简单的介绍一下基于SpringBoot框架动态多数据源切换的实现,我们创建了3个数据库用于测试,master、slave01两个数据库是mysql,slave02是sqlserver数据库。
引入相关依赖
引入的依赖是干什么的,大家看注释就行。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 添加mybatis依赖 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.5</version>
</dependency>
<!--添加mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
<scope>runtime</scope>
</dependency>
<!--sqlserver-->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>6.4.0.jre8</version>
</dependency>
<!--添加数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.16</version>
</dependency>
<!--开启aop所需的依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--代码简化工具-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<scope>provided</scope>
</dependency>
</dependencies>
配置数据库信息
在application.yml
配置3个数据库信息的信息
server:
port: 8088 # 配置端口
servlet:
context-path: /moyundong # 配置项目名称
# 配置数据源
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
# 主库数据源
master:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:1433:3306/springbootdata
username: root
password: root
# 从库数据源
slave01:
enabled: true
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:1433:3306/dh
username: root
password: root
# 测试其它不同类型数据库
slave02:
enabled: true
driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=dh-localtest
username: sa
password: root
# 配置数据源
mybatis:
mapper-locations: classpath:com/mapper/*.xml
type-aliases-package: com.moyundong.entity # 默认别名就是类名,或者类名首字母小写 SysUser | sysUser
数据源枚举类
/**
* 创建数据源枚举类
*/
public enum DataSourceType {
/**
* 主库
*/
MASTER,
/**
* 从库01
*/
SLAVE01,
/**
* 从库02
*/
SLAVE02
}
数据源切换
/**
* 数据源切换处理类,有对数据源变量的获取、设置和清空的方法
*/
@Slf4j
public class DataSourceContextHolder {
/**
* 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
*/
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
/**
* 设置数据源变量
* @param dataSourceType
*/
public static void setDataSourceType(String dataSourceType){
log.info("切换到{}数据源", dataSourceType);
CONTEXT_HOLDER.set(dataSourceType);
}
/**
* 获取数据源变量
* @return
*/
public static String getDataSourceType(){
return CONTEXT_HOLDER.get();
}
/**
* 清空数据源变量
*/
public static void clearDataSourceType(){
CONTEXT_HOLDER.remove();
}
}
动态数据源
public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* 根据Key获取数据源的信息
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceType();
}
}
数据源配置
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.master")
public DataSource masterDataSource() {
//return DataSourceBuilder.create().build();如果用这个创建的话,使用的是HikariPool连接池,数据库配置文件里面的url要写成jdbc-url
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.slave01")
@ConditionalOnProperty(prefix = "spring.datasource.slave01", name = "enabled", havingValue = "true")
public DataSource slave01DataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.slave02")
@ConditionalOnProperty(prefix = "spring.datasource.slave02", name = "enabled", havingValue = "true")
public DataSource slave02DataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "dynamicDataSource")
@Primary
public DynamicDataSource dataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
//配置默认数据源
dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
//配置多数据源
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource());
targetDataSources.put(DataSourceType.SLAVE01.name(), slave01DataSource());
targetDataSources.put(DataSourceType.SLAVE02.name(), slave02DataSource());
dynamicDataSource.setTargetDataSources(targetDataSources);
return dynamicDataSource;
}
}
这里要注意,创建DataSource
的时候有两种方式
- 使用
DataSourceBuilder .create().build();
那么意味着使用的是HikariPool
连接池,
就算在application.yml
文件里面配置的druid
也还是用的HikariPool
,这时候数据库连接的url
要写成jdbc-url
- 使用
DruidDataSourceBuilder .create().build();
使用的就是druid
的连接池,这时候数据库连接的url
就要用url
自定义数据源切换注解
自定义一个数据源切换的注解,方便在方法上使用。默认是MASTER类型数据库
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSourceRegister {
/**
* 切换数据源名称
*/
DataSourceType value() default DataSourceType.MASTER;
}
AOP拦截实现切换
定义一个拦截类,拦截DataSourceRegister注解,根据注解的参数类型切换数据源,然后执行方法。
@Aspect
@Order(1)
@Component
public class DynamicDataSourceAspect {
/**
* 定义切点Pointcut
*/
@Pointcut("@annotation(com.moyundong.dynamicdata.DataSourceRegister)")
public void excudeService() {
}
/**
*
* @param point
* @return
* @throws Throwable
*/
@Around("excudeService()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
DataSourceRegister dataSource = method.getAnnotation(DataSourceRegister.class);
if (dataSource != null) {
DataSourceContextHolder.setDataSourceType(dataSource.value().name());
}
try {
return point.proceed();
} finally {
// 销毁数据源 在执行方法之后
DataSourceContextHolder.clearDataSourceType();
}
}
}
测试service
我们在service里面写了3个查询方法,分别查询不同的数据库,为了测试方便,3个数据库的表我都用的是一样的sys_user
@Override
public List<SysUser> findAllFromMaster() {
return sysUserDao.findAll();
}
@Override
@DataSourceRegister(value = DataSourceType.SLAVE01)
public List<SysUser> findAllFromSlave01() {
return sysUserDao.findAll();
}
@Override
@DataSourceRegister(value = DataSourceType.SLAVE02)
public List<SysUser> findAllFromSlave02() {
return sysUserDao.findAll();
}
测试controller
测试方法里面,根据请求的参数type的不同,调用不同的方法查询数据。
@RequestMapping("findAll/{type}")
public Object findAll(@PathVariable(name = "type") String type){
if (DataSourceType.SLAVE01.name().equals(type)){
return sysUserService.findAllFromSlave01();
} else if(DataSourceType.SLAVE02.name().equals(type)){
return sysUserService.findAllFromSlave02();
} else{
return sysUserService.findAllFromMaster();
}
}
::: tip 提示
其它业务逻辑的代码就不写了,大家可以自己写,也可以下载整个工程文件看看。
:::
启动类
启动的时候报The dependencies of some of the beans in the application context form a cycle
错误,意思就是依赖形成了循环,解决
办法就是在启动类加上@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
和@Import({DataSourceConfig.class})
@Import({DataSourceConfig.class})
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
@MapperScan("com.moyundong.dao")
public class Springboot2Test06Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Springboot2Test06Application.class, args);
}
}
测试结果
启动工程后,在浏览器分别运行下面3个连接,会分别从3个不同的数据库查询数据,为了查看效果,每个数据库我们最好值都不一样。
http://127.0.0.1:8088/moyundong/sysUser/findAll/MASTER
http://127.0.0.1:8088/moyundong/sysUser/findAll/SLAVE01
http://127.0.0.1:8088/moyundong/sysUser/findAll/SLAVE02
使用DataSourceBuilder .create().build();
创建运行结果如下:
2020-06-10 23:24:20.933 INFO 40560 --- [nio-8088-exec-4] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2020-06-10 23:24:21.388 INFO 40560 --- [nio-8088-exec-4] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2020-06-10 23:24:43.685 INFO 40560 --- [nio-8088-exec-8] c.m.dynamicdata.DataSourceContextHolder : 切换到SLAVE01数据源
2020-06-10 23:24:43.686 INFO 40560 --- [nio-8088-exec-8] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Starting...
2020-06-10 23:24:44.285 INFO 40560 --- [nio-8088-exec-8] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Start completed.
2020-06-10 23:24:53.192 INFO 40560 --- [nio-8088-exec-9] c.m.dynamicdata.DataSourceContextHolder : 切换到SLAVE02数据源
2020-06-10 23:24:53.192 INFO 40560 --- [nio-8088-exec-9] com.zaxxer.hikari.HikariDataSource : HikariPool-3 - Starting...
2020-06-10 23:24:53.940 INFO 40560 --- [nio-8088-exec-9] com.zaxxer.hikari.HikariDataSource : HikariPool-3 - Start completed.
使用DruidDataSourceBuilder .create().build();
创建运行结果如下:
2020-06-10 23:54:39.380 INFO 45456 --- [nio-8088-exec-4] c.m.dynamicdata.DataSourceContextHolder : 切换到SLAVE01数据源
2020-06-10 23:54:39.445 INFO 45456 --- [nio-8088-exec-4] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} inited
2020-06-10 23:54:48.864 INFO 45456 --- [io-8088-exec-10] c.m.dynamicdata.DataSourceContextHolder : 切换到SLAVE02数据源
2020-06-10 23:54:48.866 INFO 45456 --- [io-8088-exec-10] com.alibaba.druid.pool.DruidDataSource : {dataSource-2} inited
2020-06-10 23:54:54.538 INFO 45456 --- [nio-8088-exec-2] com.alibaba.druid.pool.DruidDataSource : {dataSource-3} inited
1介绍
2springboot定时任务
3springboot定时任务配置详解
4springboot动态定时任务
5springboot集成websocket
6springboot多数据源
7springboot配置druid监听
8springboot自定义注解
9springboot常见注解详解
10springboot接收参数详解
11springboot验证机制@Valid和@Validated
12springboot集成Swagger2
13springboot集成swagger-bootstrap-ui
14springboot集成shiro
15springboot集成shiro(二)
16springboot集成jwt
17springboot集成ActiveMQ
18springboot缓存机制
🍉🍉🍉 欢迎大家来博客了解更多内容:java乐园 🍉🍉🍉
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/13510.html