1、实现动态切换数据源
2、实现配置多数据源
3、实现读写分离也可以用多数据源方式
4、选择
dynamic-datasource集成了很多ORM的框架,其中,使用比较多的是druid,但有一些东西开始收费了
druid也可以自行配置,配置多了点
目前版本只支持单一位置加载数据源(只能从配置文件或者自定义加载数据源),不能多位置加载数据源
目前版本未实现动态添加数据源(在切换数据源时,不存在的数据源在数据库查询,添加进数据源连接池)
springboot版本:2.3.1.RELEASE
dynamic-datasource版本:3.5.1
官方文档:https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611
实现源代码地址
<dependency>
<groupId>com.baomidougroupId>
<artifactId>dynamic-datasource-spring-boot-starterartifactId>
<version>3.5.1version>
dependency>
spring:
application:
name: microservice-boot-common
# Jackson 配置项
jackson:
# serialization:
# write-dates-as-timestamps: true # 设置 Date 的格式,使用时间戳
# write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401
# write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳
# fail-on-empty-beans: false # 允许序列化无属性的 Bean
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
datasource:
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
datasource:
druid: # Druid 【监控】相关的全局配置
web-stat-filter:
enabled: true
stat-view-servlet:
enabled: true
allow: # 设置白名单,不填则允许所有访问
url-pattern: /druid/*
login-username: test
login-password: test
filter:
stat:
enabled: true
log-slow-sql: true # 慢 SQL 记录
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
initial-size: 4 # 初始连接数
min-idle: 4 # 最小连接池数量
max-active: 50 # 最大连接池数量
max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒
time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒
min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒
max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒
validation-query: SELECT 1 # 配置检测连接是否有效
test-while-idle: true
test-on-borrow: false
test-on-return: false
name: master
master:
driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowPublicKeyRetrieval=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true&nullCatalogMeansCurrent=true
username: root
password: 123456
slave_1:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test2?useUnicode=true&allowPublicKeyRetrieval=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true&nullCatalogMeansCurrent=true
username: root
password: 123456
# MyBatis Plus 的配置项
mybatis-plus:
configuration:
map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
global-config:
db-config:
# id-type: NONE # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。
id-type: AUTO # 自增 ID,适合 MySQL 等直接自增的数据库
# id-type: INPUT # 用户输入 ID,适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库
# id-type: ASSIGN_ID # 分配 ID,默认使用雪花算法。注意,Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时,需要去除实体类上的 @KeySequence 注解
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
logic-delete-field: deleted_tag
table-prefix: t_
banner: false
type-aliases-package: org.lwd.microservice.boot.common.dao
mapper-locations: classpath*:/mapper/**/*.xml
@DS("slave_1")
@Override
public BaseResult<TenantDataSourceDTO> getTenantDataSourceByPk(String pk) {
BaseResult<TenantDataSourceDTO> baseResult = BaseResult.success();
TenantDataSource domain = this.getById(pk);
baseResult.setData(TenantDataSourceConvertor.INSTANCE.toDTO(domain));
return baseResult;
}
package org.lwd.microservice.boot.common.aop;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
/**
* 数据源拦截-aop模式
*
* @author weidong
* @version V1.0.0
* @since 2023/6/13
*/
@Order(1)
@Aspect
@EnableAspectJAutoProxy(proxyTargetClass = true)
@Component
@Slf4j
public class DataSourceChangeAdvisor {
/**
* 按需设置需要切换的模式
*/
@Pointcut("@within(org.springframework.web.bind.annotation.RestController)")
public void pointDataSource() {
}
/**
* 需要定义模块的规则
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("pointDataSource()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
try {
//获取当前请求对象
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null) {
HttpServletRequest request = attributes.getRequest();
String uri = request.getRequestURI();
//String url = request.getRequestURL().toString();
String[] uriArr = uri.split("/");
if (uriArr[uriArr.length - 1].equals("detail")) {
log.info("datasource1------");
DynamicDataSourceContextHolder.push("master");
} else {
log.info("datasource2------");
DynamicDataSourceContextHolder.push("slave_1");
}
}
} catch (Exception e) {
log.error("日志拦截异常", e);
} finally {
return joinPoint.proceed();
}
}
}
package org.lwd.microservice.boot.common.interceptor;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 动态数据源-拦截器模式-需要自定义规则
* @author weidong
* @version V1.0.0
* @since 2023/6/16
*/
@Slf4j
public class DynamicDataSourceChangeInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String uri = request.getRequestURI();
//String url = request.getRequestURL().toString();
String[] uriArr = uri.split("/");
if (uriArr[uriArr.length - 1].equals("detail")) {
log.info("interceptor datasource1------");
DynamicDataSourceContextHolder.push("master");
} else {
log.info("interceptor datasource2------");
DynamicDataSourceContextHolder.push("slave_1");
}
return true;
}
}
package org.lwd.microservice.boot.common.interceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* mvc拦截器-自定义拦截路径
* @author weidong
* @version V1.0.0
* @since 2023/6/16
*/
@Configuration
public class DynamicDataSourceChangeInterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//如果拦截全部可以设置为 /**
String [] path = {"/tenantDataSource/**"};
//不需要拦截的接口路径
String [] excludePath = {};
DynamicDataSourceChangeInterceptor dynamicDataSourceChangeInterceptor = new DynamicDataSourceChangeInterceptor();
registry.addInterceptor(dynamicDataSourceChangeInterceptor).addPathPatterns(path).excludePathPatterns(excludePath);
}
}
这种方式是配置文件的方式,可对立的配置文件配置多个数据源,
但是动态增加或者减少数据源,需要自己实现一部分代码,看下一篇文章
原创不易,如若本文能够帮助到您的同学
支持我:关注我+点赞+收藏⭐️
留言:探讨问题,看到立马回复
格言:己所不欲勿施于人 扬帆起航、游历人生、永不言弃!