多数据源系统接⼊mybatis-plus,实现动态数据源、动态事务。⽬录:
实现思想
导⼊依赖、配置说明
代码实现
问题总结
⼀.实现思想
  接⼿⼀个旧系统,SpringBoot 使⽤的是纯粹的 mybatis ,既没有使⽤规范的代码⽣成器,也没有使⽤ JPA 或者mybatis-plus。
  想着接⼊ mybatis-plus,为以后敲代码省点⼒⽓。普通的接⼊ mybatis-plus 可以直接参考官⽅⽂档。
  但我接⼿的系统是个多数据源系统,本来最优的⽅法是使⽤官⽅的动态数据源⽀持。
  但我因为乱七⼋糟的依赖冲突,决定⾃⼰实现动态数据源的⽀持。
  实现的核⼼逻辑:使⽤⼀个代理数据源,来管理其他数据源的分发请求。(通过AOP分发)
⼆.导⼊依赖、配置说明
  因为依赖的冲突,我没有直接使⽤
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.p</version>
</dependency>
  ⽽是引⼊的
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.p</version>
</dependency>
  数据库配置⽂件⼤致如下
spring:
datasource: #数据库配置
primary: #数据库1
driver-class-name: sql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://
username: root
password: 3
type: com.alibaba.druid.pool.DruidDataSource
second: #数据库2
driver-class-name: sql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://
username: root
password: 3
type: com.alibaba.druid.pool.DruidDataSource
third: #数据库3
driver-class-name: sql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://
username: root
password: 3
type: com.alibaba.druid.pool.DruidDataSource
  MybatisPlus 配置⼤致如下(mybatis 的配置可以删除)
mybatis-plus:
# 扫描 l
mapper-locations: classpath:mapper/*.xml #也可以不配置,在代码中设置
#  configuration:
#    map-underscore-to-camel-case: false
三.代码实现
  1.我们先新建数据源的枚举
public enum DataSourceEnums {
PRIMARY("primaryDataSource"),
SECOND("secondDataSource"),
jpa mybatisTHIRD("thirdDataSource");
private String value;
DataSourceEnums(String value){this.value=value;}
public String getValue() {
return value;
}
}
  2.⽤来标记数据源的注解(在哪⾥使⽤哪个数据源)。
/**
* @author zhaww
* @date 2020/4/14
* @Description .⾃定义 - 区分数据源的注解
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyDataSource {
DataSourceEnums value() default DataSourceEnums.PRIMARY;
}
  3.动态数据源管理器,继承 AbstractRoutingDataSource
/**
* @author zhaww
* @date 2020/4/10
* @Description .动态数据源管理器
*/public class DataSourceContextHolder extends AbstractRoutingDataSource {
private static final ThreadLocal<String> contextHolder = new InheritableThreadLocal<>(); /**
* 重写这个⽅法,这⾥返回使⽤的数据源 key 值
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
//        log.info("动态切换数据源:" + DataSource());
();
}
/**
*  设置数据源
* @param db
*/
public static void setDataSource(String db){
contextHolder.set(db);
}
/**
* 取得当前数据源
* @return
*/
public static String getDataSource(){
();
}
/**
* 清除上下⽂数据
*/
public static void clear(){
}
}
  4. mybatis-plus 的配置类
/**
* @author zhaww
* @date 2020/4/10
* @Description .
*/
//@EnableTransactionManagement //开启事务
@Configuration
@MapperScan(value = {"dd.admin.dao"}) //扫描Mapper 层的类
public class MybatisPlusConfig {
@Bean(name = "primaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
ate().build();
}
@Bean(name = "secondDataSource")
@ConfigurationProperties(prefix = "spring.datasource.second")
public DataSource secondDataSource() {
ate().build();
}
@Bean(name = "thirdDataSource")
@ConfigurationProperties(prefix = "spring.datasource.third")
public DataSource thirdDataSource() {
ate().build();
}
@Bean(name = "multipleTransactionManager")
@Primary
public DataSourceTransactionManager multipleTransactionManager(@Qualifier("multipleDataSource") DataSource dataSource) { //        return new MyDataSourceTransactionManager(dataSource);
return new DataSourceTransactionManager(dataSource);
}
/**
* 动态数据源配置
*
* @return
*/
@Bean(name = "multipleDataSource")
@Primary
public DataSource multipleDataSource(@Qualifier("primaryDataSource") DataSource primaryDataSource,
@Qualifier("secondDataSource") DataSource secondDataSource,
@Qualifier("thirdDataSource") DataSource thirdDataSource) {
DataSourceContextHolder dynamicDataSource = new DataSourceContextHolder();
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(Value(), primaryDataSource);
targetDataSources.put(Value(), secondDataSource);
targetDataSources.put(Value(), thirdDataSource);
dynamicDataSource.setTargetDataSources(targetDataSources);
dynamicDataSource.setDefaultTargetDataSource(thirdDataSource); // 默认使⽤的数据源
return dynamicDataSource;
}
@Bean("sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory() throws Exception {
MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
sqlSessionFactory.setDataSource(multipleDataSource(primaryDataSource(), secondDataSource(), thirdDataSource()));
      //mybatis-plus yml 配置不⽣效,要在这⾥代码⾥配置
MybatisConfiguration configuration = new MybatisConfiguration();
configuration.setJdbcTypeForNull(JdbcType.NULL);
//是否使⽤转驼峰
configuration.setMapUnderscoreToCamelCase(false);
configuration.setCacheEnabled(false);
sqlSessionFactory.setConfiguration(configuration);
//添加分页功能
Interceptor[] plugins = {paginationInterceptor()};
sqlSessionFactory.setPlugins(plugins);
//扫描 mapper 路径
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();        Resource[] resource = Resources("classpath:mapper/**/*.xml");
sqlSessionFactory.setMapperLocations(resource);
Object();
}
/**
* @Description : mybatis-plus分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
/
/ 设置请求的页⾯⼤于最⼤页后操作, true调回到⾸页,false 继续请求默认false        paginationInterceptor.setOverflow(true);
// 设置最⼤单页限制数量,默认 500 条,-1 不受限制
//        paginationInterceptor.setLimit(30);
return paginationInterceptor;
}
}
  5.使⽤AOP来实现数据源的动态设置。
/**
* @author zhaww
* @date 2020/4/9
* @Description .AOP通⽤⽇志记录、动态数据源分发
*/
@Aspect
@Component
@Slf4j
@Order(-100)
public class AOP {
/**
* Controller层路径
*/
@Pointcut("ller..*)")
public void controllerPointcut() {
}
/**
* Service层路径
*/
@Pointcut("dd.admin.service..*)")
public void servicePointcut() {
}
@Around("servicePointcut()")
public Object doServiceLogging(ProceedingJoinPoint joinPoint) throws Throwable {
changeDataSource(joinPoint); //检查数据源
}
/
**
* Mapper层拦截,动态切换 mybatisPlus 数据源
*/
@Before("execution(* dd.admin.dao.primary..*(..))")
public void doAdmin(){
log.info("选择数据源---" + Value());
DataSourceContextHolder.setDataSource(Value());    }
/**
* Mapper层拦截,动态切换 mybatisPlus 数据源
*/
@Before("execution(* dd.admin.dao.second..*(..))")
public void doZYDD(){
log.info("选择数据源---" + Value());
DataSourceContextHolder.setDataSource(Value());    }
/**
* Mapper层拦截,动态切换 mybatisPlus 数据源
*/
@Before("execution(* dd.admin.dao.third..*(..))")
public void doDW(){
log.info("选择数据源---" + Value());
DataSourceContextHolder.setDataSource(Value());
}
/
**
* 通过注解变更数据源
* @param joinPoint
*/
private void changeDataSource(ProceedingJoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) Signature();
Method method = Method();
MyDataSource myDataSource = null;
//优先判断⽅法上的注解
if (method.isAnnotationPresent(MyDataSource.class)) {
myDataSource = Annotation(MyDataSource.class);
DataSourceContextHolder.setDataSource(myDataSource.value().getValue());
} else if (DeclaringClass().isAnnotationPresent(MyDataSource.class)) { //其次判断类上的注解
myDataSource = DeclaringClass().getAnnotation(MyDataSource.class);
DataSourceContextHolder.setDataSource(myDataSource.value().getValue());
}
     if (myDataSource != null) {
     log.info("注解⽅式选择数据源---" + myDataSource.value().getValue());
     }
  }
}
 注意:我们不但默认通过 Mapper 的路径来切换数据源,还通过 Service ⽅法层来切换数据源。
  因为如果 service 有事务的话,进⼊service⽅法的时候,DataSourceTransactionManager 就设置好了默认数据源,就算通过Mapper层重新设置数据源,
  DataSourceTransactionManager的默认数据源还是没有变。
  所以在事务管理器设置默认数据源之前,就切换数据源,实现动态事务+动态数据源。
  6.实际使⽤,只要MyDataSource注解就ok了。也可以在 ServiceImpl 类上加注解。
@Override
@MyDataSource(DataSourceEnums.THIRD)
@Transactional
public void test() {
DwUserMPEntity test2 = new DwUserMPEntity();
test2.setUuid("test");
test2.setNickname("test");
test2.setPhone("test");
dwUserDao.insert(test2);
//        throw new RuntimeException("lala");
}
@Override
@MyDataSource(DataSourceEnums.PRIMARY)
@Transactional
public void test1() {
SysRoleMPEntity test = new SysRoleMPEntity();
test.setRole("test");
test.setDescription("test");
sysRoleDao.insert(test);
//        throw new RuntimeException("lala");
}
五.问题总结
1.配置⽂件⾥ mybatis-plus的配置不⽣效:因为我们在 SqlSessionFactory ⾥重新写了 MybatisConfiguration 。
2.启⽤事务的话,动态数据源不⽣效:因为 service 有事务的话,在进⼊service⽅法
时,DataSourceTransactionManager 就设置好了默认数据源。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。