SpringBoot + MyBatis Plus + Druid + 注解方式多数据源

【简介】

如果不熟悉MyBatisPlus可以先看下这篇文章:SpringBoot + MyBatis Plus 生成代码器 (时间格式问题)
本文主要讲解使用Druid + 注解的方式配置多数据源

【引入依赖】

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-aopartifactId>
dependency>
<dependency>
    <groupId>com.baomidougroupId>
    <artifactId>mybatis-plus-boot-starterartifactId>
    <version>3.3.2version>
dependency>

<dependency>
    <groupId>mysqlgroupId>
    <artifactId>mysql-connector-javaartifactId>
    <scope>runtimescope>
dependency>


<dependency>
    <groupId>com.alibabagroupId>
    <artifactId>druid-spring-boot-starterartifactId>
    <version>1.2.2version>
dependency>
<dependency>
    <groupId>org.projectlombokgroupId>
    <artifactId>lombokartifactId>
    <optional>trueoptional>
dependency>

【application.yml】

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    druid:
      # 主库数据源
      master:
        url: jdbc:mysql://localhost:3306/kyx?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
        username: root
        password: 123456
      # 从库数据源
      slave:
        # 从数据源开关/默认关闭
        enabled: false
        url: jdbc:mysql://localhost:3306/qiankyx?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
        username: root
        password: 123456
      # 初始连接数
      initialSize: 5
      # 最小连接池数量
      minIdle: 10
      # 最大连接池数量
      maxActive: 20
      # 配置获取连接等待超时的时间
      maxWait: 60000
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      timeBetweenEvictionRunsMillis: 60000
      # 配置一个连接在池中最小生存的时间,单位是毫秒
      minEvictableIdleTimeMillis: 300000
      # 配置一个连接在池中最大生存的时间,单位是毫秒
      maxEvictableIdleTimeMillis: 900000
      # 配置检测连接是否有效
      validationQuery: SELECT 1 FROM DUAL
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      webStatFilter:
        enabled: true
      statViewServlet:
        enabled: true
        # 设置白名单,不填则允许所有访问
        allow:
        url-pattern: /druid/*
        # 控制台管理用户名和密码
        login-username: admin
        login-password: kyx123456
      filter:
        stat:
          enabled: true
          # 慢SQL记录
          log-slow-sql: true
          slow-sql-millis: 1000
          merge-sql: true
        wall:
          config:
            multi-statement-allow: true

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    auto-mapping-behavior: full
  mapper-locations: classpath*:mapper/**/*Mapper.xml
  type-aliases-package: com.kyx.orderSys.biz.**.model
  global-config:
    db-config:
      logic-not-delete-value: 1
      #逻辑删除配置
      logic-delete-value: 0
      #数据库大写下划线转换
      capital-mode: true

【DataSourceType.java】

添加数据源枚举类, 与yml数据库对应. 可根据业务需求, 自定义增加相应的值

/**
 * 数据源
 *
 * @author xyang
 */
public enum DataSourceType
{
     
    /**
     * 主数据源
     */
    MASTER,

    /**
     * 从数据源
     */
    SLAVE
}

【DynamicDataSource.java】

数据源切换处理类. 用于记录与切换数据源

public class DynamicDataSource extends AbstractRoutingDataSource
{
     
    public static final Logger log = LoggerFactory.getLogger(DynamicDataSource.class);

	@Override
    protected Object determineCurrentLookupKey()
    {
     
        return getDataSourceType();
    }
   
    /**
     * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
     *  所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
     */
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    /**
     * 设置数据源的变量
     */
    public static void setDataSourceType(String dsType)
    {
     
        log.info("切换到{}数据源", dsType);
        contextHolder.set(dsType);
    }

    /**
     * 获得数据源的变量
     */
    public static String getDataSourceType()
    {
     
        return contextHolder.get();
    }

    /**
     * 清空数据源变量
     */
    public static void clearDataSourceType()
    {
     
        contextHolder.remove();
    }
}

【DruidConfig.java】

druid 配置多数据源, 设置动态数据源

@Configuration
public class DruidConfig
{
     
    @Bean
    @ConfigurationProperties("spring.datasource.druid.master")
    public DataSource masterDataSource() {
     
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.druid.slave")
    @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
    public DataSource slaveDataSource(DruidProperties druidProperties) {
     
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "dynamicDataSource")
    @Primary
    public DynamicDataSource dataSource(DataSource masterDataSource) {
     
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
        setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
        return new DynamicDataSource(masterDataSource, targetDataSources);
    }

    /**
     * 设置数据源
     *
     * @param targetDataSources 备选数据源集合
     * @param sourceName 数据源名称
     * @param beanName bean名称
     */
    public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName)
    {
     
        try
        {
     
            DataSource dataSource = SpringUtils.getBean(beanName);
            targetDataSources.put(sourceName, dataSource);
        }
        catch (Exception e)
        {
     
        }
    }
}

注意事项: ConfigurationProperties中的值需与yml文件中的路径相对应

【DataSource.java】

自定义多数据源切换注解
优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准

@Target({
      ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DataSource
{
     
    /**
     * 切换数据源名称
     */
    public DataSourceType value() default DataSourceType.MASTER;
}

【DataSourceAspect.java】

多数据源处理. 设置切点, 根据上面定义的DataSource注解, 动态切换数据源

@Aspect
@Order(1)
@Component
public class DataSourceAspect {
     
    protected Logger logger = LoggerFactory.getLogger(getClass());

    @Pointcut("@annotation(com.kyx.orderSys.base.annotation.DataSource)"
            + "|| @within(com.kyx.orderSys.base.annotation.DataSource)")
    public void dsPointCut() {
     

    }

    @Around("dsPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
     
        DataSource dataSource = getDataSource(point);

        if (StringUtils.isNotNull(dataSource)) {
     
            DynamicDataSource.setDataSourceType(dataSource.value().name());
        }

        try {
     
            return point.proceed();
        } finally {
     
            // 销毁数据源 在执行方法之后
            DynamicDataSource.clearDataSourceType();
        }
    }

    /**
     * 获取需要切换的数据源
     */
    public DataSource getDataSource(ProceedingJoinPoint point) {
     
        MethodSignature signature = (MethodSignature) point.getSignature();
        DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
        if (Objects.nonNull(dataSource)) {
     
            return dataSource;
        }

        return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
    }
}

【注解的使用】

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
     

    @Override
    @DataSource(DataSourceType.SLAVE)
    public IPage<User> getFastUsers(Page<User> page, Wrapper<User> queryWrapper) {
     
        return baseMapper.selectPage(page, queryWrapper);
    }
    
 	@Override
    @DataSource(DataSourceType.SLAVE)
    public boolean removeById(Serializable id) {
     
        return super.removeById(id);
    }
    
    // 此处@DataSource注解可省略
    @Override
    @DataSource(DataSourceType.MASTER)
    public boolean updateById(User entity) {
     
        return super.updateById(entity);
    }
}

【注意事项】

使用druid多数据源时,项目启动出现数据源循环依赖
SpringBoot + MyBatis Plus + Druid + 注解方式多数据源_第1张图片

解决办法: sprintboot 低版本可以使用 spring.datasource.initialize=false (默认为true) 来解决,升级到sprintboot2.1.2后就发现被弃用了, 可在启动类上增加这个exclude = { DataSourceAutoConfiguration.class} 属性即可

@SpringBootApplication(exclude = {
      DataSourceAutoConfiguration.class})
@MapperScan("com.kyx.orderSys.biz.**.mapper")
public class CyyApplication {
     

    public static void main(String[] args) {
     
        SpringApplication.run(CyyApplication.class, args);
    }

}

你可能感兴趣的:(mybatis,plus,springboot,java,idea,spring,boot,mybatis,后端)