SpringBoot+MyBatisPlus+Druid读写分离全配置

目录

一、配置流程

二、YML配置文件

三、Druid配置类详情,可拆分对应配置

四、涉及工具类(注解、切面、枚举、保存当前线程的数据源类型)

五、参考文档地址


一、配置流程

1、yml增加配置数据源配置信息;

2、配置对应数据源的DataSource对象;

3、配置对应的AbstractRoutingDataSource对象信息;

4、配置SqlSessionFactory配置信息,需要将路由配置信息配置于对应的SqlSession中;

5、配置对应的数据源事务管理器;

6、方法上增加注解,切换数据源。

二、YML配置文件


spring:
  #数据库配置
  datasource:
    username: ****
    password: ****
    url: jdbc:mysql://****:3306/database_master?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    #druid 数据源专有配置
    druid:
      #初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
      initial-size: 20
      #最小连接池数量
      min-idle: 20
      #最大连接池数量
      max-active: 80
      #获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
      max-wait: 60000
      #有两个含义: 1) Destroy线程会检测连接的间隔时间2) testWhileIdle的判断依据,详细看testWhileIdle属性的说明
      timeBetweenEvictionRunsMillis: 60000
      #连接保持空闲⽽而不不被驱逐的最⻓长时间
      minEvictableIdleTimeMillis: 300000
      # 校验SQL,Oracle配置 spring.datasource.validationQuery=SELECT 1 FROM DUAL,如果不配validationQuery项,则下面三项配置无用
      validation-query: SELECT 'X'
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      # 打开PSCache,并且指定每个连接上PSCache的大小
      pool-prepared-statements: true
      max-pool-prepared-statement-per-connection-size: 20
      # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
      filters: stat,wall,slf4j
      # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
      connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
      # 合并多个DruidDataSource的监控数据
      use-global-data-source-stat: true
    mysql-salve:
      username: ****
      password: ****
      url: jdbc:mysql://****:3306/database_slave?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
      driver-class-name: com.mysql.cj.jdbc.Driver

三、Druid配置类详情,可拆分对应配置

import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.MybatisXMLLanguageDriver;
import com.yswl.smart.tool.aspect.EnumDataSourceType;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.JdbcType;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

/**
 * 类文件描述:DruidConfig
 *
 * @date 2022/02/22 10:31
 */
@Configuration
@Slf4j
public class DruidConfig {

    @Value("${spring.datasource.url}")
    private String masterDbUrl;
    @Value("${spring.datasource.username}")
    private String masterUsername;
    @Value("${spring.datasource.password}")
    private String masterPassword;
    @Value("${spring.datasource.driver-class-name}")
    private String masterDriverClassName;

    @Value("${spring.datasource.mysql-slave.url}")
    private String slaveDbUrl;
    @Value("${spring.datasource.mysql-slave.username}")
    private String slaveUsername;
    @Value("${spring.datasource.mysql-slave.password}")
    private String slavePassword;
    @Value("${spring.datasource.mysql-slave.driver-class-name}")
    private String slaveDriverClassName;

    @Value("${spring.datasource.druid.initial-size}")
    private int initialSize;
    @Value("${spring.datasource.druid.min-idle}")
    private int minIdle;
    @Value("${spring.datasource.druid.max-active}")
    private int maxActive;
    @Value("${spring.datasource.druid.max-wait}")
    private int maxWait;
    @Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}")
    private int timeBetweenEvictionRunsMillis;
    @Value("${spring.datasource.druid.minEvictableIdleTimeMillis}")
    private int minEvictableIdleTimeMillis;
    @Value("${spring.datasource.druid.validation-query}")
    private String validationQuery;
    @Value("${spring.datasource.druid.test-while-idle}")
    private boolean testWhileIdle;
    @Value("${spring.datasource.druid.test-on-borrow}")
    private boolean testOnBorrow;
    @Value("${spring.datasource.druid.test-on-return}")
    private boolean testOnReturn;
    @Value("${spring.datasource.druid.pool-prepared-statements}")
    private boolean poolPreparedStatements;
    @Value("${spring.datasource.druid.max-pool-prepared-statement-per-connection-size}")
    private int maxPoolPreparedStatementPerConnectionSize;
    @Value("${spring.datasource.druid.filters}")
    private String filters;
    @Value("${spring.datasource.druid.connection-properties}")
    private String connectionProperties;
    @Value("${spring.datasource.druid.use-global-data-source-stat}")
    private boolean useGlobalDataSourceStat;

    @Bean
    @Primary
    public DataSource masterDataSource() {
        DruidDataSource datasource = new DruidDataSource();
        datasource.setUrl(this.masterDbUrl);
        datasource.setUsername(this.masterUsername);
        datasource.setPassword(this.masterPassword);
        datasource.setDriverClassName(this.masterDriverClassName);
        return this.creatDataSource(datasource);

    }

    @Bean(name = "slaveDataSource")
    public DataSource slaveOneDataSource() {
        DruidDataSource datasource = new DruidDataSource();
        datasource.setUrl(this.slaveDbUrl);
        datasource.setUsername(this.slaveUsername);
        datasource.setPassword(this.slavePassword);
        datasource.setDriverClassName(this.slaveDriverClassName);
        return this.creatDataSource(datasource);
    }

    @Bean
    public AbstractRoutingDataSource routingDataSource() {
        Map targetDataSources = new HashMap<>(2);
        DataSource masterDataSource = this.masterDataSource();
        targetDataSources.put(EnumDataSourceType.MYSQL_MASTER.getValue(), masterDataSource);
        targetDataSources.put(EnumDataSourceType.MYSQL_SLAVE.getValue(), this.slaveOneDataSource());
        // 路由类,寻找对应的数据源
        AbstractRoutingDataSource proxy = new AbstractRoutingDataSource() {
            @Override
            protected Object determineCurrentLookupKey() {
                String key = DataSourceContextHolder.getDataSourceRoutingKey();
                if (key == null) {
                    return EnumDataSourceType.MYSQL_MASTER.getValue();
                }
                return key;
            }
        };
        // 默认库
        proxy.setDefaultTargetDataSource(masterDataSource);
        proxy.setTargetDataSources(targetDataSources);
        return proxy;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(this.routingDataSource());
        // 配置扫描实体类路径
        sqlSessionFactoryBean.setTypeAliasesPackage("com.model.entity.*");

        MybatisConfiguration configuration = new MybatisConfiguration();
        configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);
        configuration.setJdbcTypeForNull(JdbcType.NULL);
        sqlSessionFactoryBean.setConfiguration(configuration);

        sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
        return sqlSessionFactoryBean.getObject();
    }

    /**
     * 方法描述:配置事务管理器
     *
     * @return org.springframework.jdbc.datasource.DataSourceTransactionManager
     * @date 2022/2/22 14:16
     */
    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager() {
        return new DataSourceTransactionManager(this.routingDataSource());
    }


    /**
     * 方法描述:配置数据源参数
     *
     * @param datasource datasource
     * @return javax.sql.DataSource
     * @date 2022/2/22 14:00
     */
    private DataSource creatDataSource(DruidDataSource datasource) {
        //configuration
        datasource.setInitialSize(initialSize);
        datasource.setMinIdle(minIdle);
        datasource.setMaxActive(maxActive);
        datasource.setMaxWait(maxWait);
        datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        datasource.setValidationQuery(validationQuery);
        datasource.setTestWhileIdle(testWhileIdle);
        datasource.setTestOnBorrow(testOnBorrow);
        datasource.setTestOnReturn(testOnReturn);
        datasource.setPoolPreparedStatements(poolPreparedStatements);
        datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
        datasource.setUseGlobalDataSourceStat(useGlobalDataSourceStat);
        try {
            datasource.setFilters(filters);
        } catch (SQLException e) {
            log.warn("druid configuration initialization filter: " + e);
        }
        datasource.setConnectionProperties(connectionProperties);
        return datasource;
    }
}

四、涉及工具类(注解、切面、枚举、保存当前线程的数据源类型)


import java.lang.annotation.*;

/**
 * 类文件描述:数据源切换注解类,默认主数据源
 *
 * @date 2022/02/22 14:00
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface DBType {

    EnumDataSourceType value() default EnumDataSourceType.MYSQL_MASTER;
}

import com.yswl.smart.tool.annotation.DBType;
import com.yswl.smart.tool.config.DataSourceContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;


/**
 * 类文件描述: 数据源切换AOP切面
 *
 * @date 2022/02/22 14:00
 */
@EnableAspectJAutoProxy
@Aspect
@Component
@Slf4j
public class DataSourceAspect {
       
   /**
     * 方法描述:注解标识方法执行前 @annotation中需要替换为自己对应的注解全路径
     *
     * @param joinPoint joinPoint
     * @date 2022/2/22 16:16
     */
    @Before("@annotation(com.tool.annotation.DBType)")
    public void before(JoinPoint joinPoint) {
        // 获取连接点签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        DBType annotation = signature.getMethod().getAnnotation(DBType.class);
        String databaseType = annotation.value().getValue();
        DataSourceContextHolder.setDataSourceType(databaseType);
    }

    @After("@annotation(com.tool.annotation.DBType)")
    public void afterMaster() {
        DataSourceContextHolder.clear();
    }
}

/**
 * 类文件描述:数据源枚举
 *
 * @date 2022/2/22 15:09
 */
public enum EnumDataSourceType {

    MYSQL_MASTER("master"),
    MYSQL_SLAVE("salve");

    /**
     * 参数类型
     */
    private String value;

    EnumDataSourceType(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }
}
import lombok.experimental.UtilityClass;

/**
 * 类文件描述:保存当前线程的数据源类型
 *
 * @date 2022/02/22 15:31
 */
@UtilityClass
public class DataSourceContextHolder {

    private static final ThreadLocal LOCAL = new ThreadLocal<>();

    public static ThreadLocal getLocal() {
        return LOCAL;
    }

    public static void setDataSourceType(String dataSourceType) {
        LOCAL.set(dataSourceType);
    }

    public static String getDataSourceRoutingKey() {
        return LOCAL.get();
    }

    public static void clear() {
        LOCAL.remove();
    }
}

五、参考文档地址

        ​​​​​​Spring Boot多数据源配置/读写分离(Druid+MyBatisPlus)_聚沙成塔-CSDN博客_springboot多数据源配置读写分离

springboot多数据源&动态数据源(主从) - xj-record - 博客园

Spring Boot多数据源配置实现读写分离_noralthank的博客-CSDN博客_springboot 多数据源 读写分离

Mybatis-Plus与Mybatis的sqlSessionFactory自定义 - 写代码其实苦的 - 博客园

AOP方法拦截获取参数上的注解 - 简书

你可能感兴趣的:(笔记,spring,boot,java)