基于mybatisplus实现数据源动态添加、删除、切换,⾃定义
数据源
简介
基于springboot,mybatis plus集成了⼀套多数据源的解决⽅案,在使⽤时引⼊相应的插件dynamic-datasource-spring-boot-starter,可以实现数据源的动态添加、删除等功能,对于多租户或者分库等操作可以根据AOP切⾯代理到不同的数据源、实现单⼀系统数据隔离的⽬的。
代码⽰例
mavne依赖
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3.4</version>
</dependency>
<!--dynamic-datasource-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
数据源增加、移除
@RestController
@RequestMapping("/datasources")
public class DataSourceController {
@Resource
private DataSource dataSource;
springboot aop@Resource
private DefaultDataSourceCreator dataSourceCreator;
@GetMapping("list")
public Set<String> list() {
DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
DataSources().keySet();
}
@PostMapping("add")
public Set<String> add(@Validated @RequestBody DataSourceDTO dto) {
DataSourceProperty dataSourceProperty = new DataSourceProperty();
DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
DataSource dataSource = ateDataSource(dataSourceProperty); ds.PollName(), dataSource);
DataSources().keySet();
}
@DeleteMapping("remove")
public void remove(String name) {
DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
}
}
默认的数据源连接池加载顺序为: druid>hikaricp>beecp>dbcp>spring basic
数据源切换
基于AOP切换
添加注解,排除不做切换的接⼝
package com.starsray.dynamic.datasource.annotation;
import java.lang.annotation.*;
/**
* <p>
* ⽤户标识仅可以使⽤默认数据源
* </p>
*
* @author starsray
* @since 2021-11-10
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DefaultDs {
}
切⾯具体实现
package com.starsray.dynamic.datasource.interceptor;
import com.baomidou.lkit.DynamicDataSourceContextHolder;
import com.starsray.dynamic.datasource.annotation.DefaultDs;
import com.starsray.ption.ExceptionEnum;
import com.starsray.ption.GlobalException;
import lombok.RequiredArgsConstructor;
import org.apachemons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.flect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.t.request.RequestAttributes;
import org.t.request.RequestContextHolder;
import org.t.request.ServletRequestAttributes;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import flect.Method;
/
**
* <p>
* 数据源选择器切⾯
* </p>
*
* @author starsray
* @since 2021-11-10
*/
@Aspect
@Component
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class DsInterceptor implements HandlerInterceptor {
@Pointcut("execution(public * com.starsray.ller.*.*(..))")
public void datasourcePointcut() {
}
/**
* 前置操作,拦截具体请求,获取header⾥的数据源id,设置线程变量⾥,⽤于后续切换数据源 */
@Before("datasourcePointcut()")
public void doBefore(JoinPoint joinPoint) {
Signature signature = Signature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = Method();
/
/ 排除不可切换数据源的⽅法
DefaultDs annotation = Annotation(DefaultDs.class);
if (null != annotation) {
DynamicDataSourceContextHolder.push("master");
} else {
RequestAttributes requestAttributes = RequestAttributes(); ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
assert attributes != null;
HttpServletRequest request = Request();
String header = Header("tenantName");
if (StringUtils.isNotBlank(header)) {
DynamicDataSourceContextHolder.push(header);
DynamicDataSourceContextHolder.push(header);
} else {
throw new GlobalException(ExceptionEnum.NOT_TENANT);
}
}
}
/**
* 后置操作,设置回默认的数据源id
*/
@AfterReturning("datasourcePointcut()")
public void doAfter() {
DynamicDataSourceContextHolder.push("master");
}
}
基于重写处理器
mybatis plus提供了默认处理器来决定使⽤的数据源,可以重写处理器实现⾃定义参数,⽐如从请求header⾥⾯获取参数切换数据源。@DS("#antId")
⾃定义处理器
public class HeaderProcessor extends DsProcessor {
private static final String HEADER = "#header";
@Override
public boolean matches(String key) {
return key.startsWith(HEADER);
}
@Override
public String doDetermineDatasource(MethodInvocation invocation, String key) {
HttpServletRequest request = ((ServletRequestAttributes) RequestAttributes()).getRequest();
Header(key.substring(8));
}
}
注册⾃定义处理器
@Configuration
public class CustomerDynamicDataSourceConfig{
@Bean
public DsProcessor dsProcessor() {
DsHeaderProcessor headerProcessor = new DsHeaderProcessor();
DsSessionProcessor sessionProcessor = new DsSessionProcessor();
DsSpelExpressionProcessor spelExpressionProcessor = new DsSpelExpressionProcessor();
headerProcessor.setNextProcessor(sessionProcessor);
sessionProcessor.setNextProcessor(spelExpressionProcessor);
return headerProcessor;
}
}
如果有场景需要⼿动切换数据源,可以使⽤组件提供的⼯具来实现。
DynamicDataSourceContextHolder.push("master");
⾃定义数据源
mybatis plus提供了⼀个接⼝来加载数据源信息。
public interface DynamicDataSourceProvider {
Map<String, DataSource> loadDataSources();
}
这个接⼝有⼀个抽象实现类AbstractDataSourceProvider,通过模板⽅法定义了加载数据源来源的⽅式,mybatis plus通过YmlDynamicDataSourceProvider实现了读取yml⽂件配置来初始化数据源的⽅式。
public abstract class AbstractDataSourceProvider implements DynamicDataSourceProvider {
private static final Logger log = Logger(AbstractDataSourceProvider.class);
@Autowired
private DefaultDataSourceCreator defaultDataSourceCreator;
public AbstractDataSourceProvider() {
}
protected Map<String, DataSource> createDataSourceMap(Map<String, DataSourceProperty> dataSourcePropertiesMap) {
Map<String, DataSource> dataSourceMap = new HashMap(dataSourcePropertiesMap.size() * 2);
Iterator var3 = Set().iterator();
while(var3.hasNext()) {
Entry<String, DataSourceProperty> item = (();
String dsName = (Key();
DataSourceProperty dataSourceProperty = (Value();
String poolName = PoolName();
if (poolName == null || "".equals(poolName)) {
poolName = dsName;
}
dataSourceProperty.setPoolName(poolName);
dataSourceMap.put(dsName, ateDataSource(dataSourceProperty));
}
return dataSourceMap;
}
}
如果有需要从数据库加载数据源信息,可以重写AbstractJdbcDataSourceProvider中的executeStmt⽅法来加载数据库配置信息。⽰例:
package com.digitalzz.dynamic.ds.provider;
import com.baomidou.dynamic.datasource.provider.AbstractJdbcDataSourceProvider;
import com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.digitalzz.fig.DefaultDsConfig;
import com.digitalzz.stant.DsDriverEnum;
import t.annotation.Bean;
import t.annotation.Configuration;
import t.annotation.Primary;
import javax.annotation.Resource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论