java每日一记 —— mybatis的动态数据源切换

动态数据源切换

  • 1.环境初始化
  • 2.切换数据源代码
  • 3.第二节代码的测试
  • 4.用注解的方式进行优化

此代码在jdk11上测试通过,SpringBoot版本为2.7.14

1.环境初始化

1.创建两个库

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- 表结构
DROP TABLE IF EXISTS `t_stu`;
CREATE TABLE `t_stu`  (
  `id` int NOT NULL COMMENT 'id',
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `age` int NULL DEFAULT NULL,
  `sex` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `class_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '课程名',
  `teacher` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '老师',
  `score` decimal(10, 2) NULL DEFAULT NULL COMMENT '分数',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

-- 库1数据
INSERT INTO `t_stu` VALUES (1, 'andy', 10, '男', '语文', '虚竹', 100.00);
-- 库2数据
INSERT INTO `t_stu` VALUES (1, 'lily', 10, '女', '数学', '小龙女', 100.00);

2.pom.xml

 <parent>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-parent</artifactId>
     <version>2.7.14</version>
     <relativePath/>
 </parent>
 <dependencies>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-data-rest</artifactId>
     </dependency>
     <dependency>
         <groupId>com.baomidou</groupId>
         <artifactId>mybatis-plus-boot-starter</artifactId>
         <version>3.5.4</version>
     </dependency>
     <dependency>
         <groupId>com.alibaba</groupId>
         <artifactId>druid-spring-boot-starter</artifactId>
         <version>1.2.16</version>
     </dependency>
     <dependency>
         <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
         <version>8.0.33</version>
     </dependency>
 </dependencies>

3.application.yml

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      db1:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/andy_test_1?characterEncoding=utf-8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&useSSL=false
        username: root
        password: root
      db2:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/andy_test_2?characterEncoding=utf-8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&useSSL=false
        username: root
        password: root

4.pojo类

public class Stu implements Serializable {
    
    private Integer id;

    private String name;

    private Integer age;

    private String className;

    private BigDecimal score;

	// getter...
	// setter...
}

5.mapper

@Mapper
public interface StuMapper  extends BaseMapper<Stu> {

    List<Stu> findAll();

    int insertStu(@Param("stu") Stu stu);

}

6.mapper.xml


DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.andy.mapper.StuMapper">

    <select id="findAll" resultType="com.andy.pojo.Stu">
        select * from t_stu
    select>

    <insert id="insertStu" parameterType="com.andy.pojo.Stu">
        insert into t_stu(
        id,
        name)
        values(
        #{stu.id},
        #{stu.name})
    insert>
mapper>

7.controller

@RestController
public class TestController {
    @Resource
    private StuMapper stuMapper;

    @GetMapping("/getData/{datasourceName}")
    public String getMasterData(@PathVariable("datasourceName") String datasourceName){
        DataSourceAndyContextHolder.setDataSource(datasourceName);
        List<Stu> all = stuMapper.findAll();
        DataSourceAndyContextHolder.removeDataSource();
        return all.get(0).getName();
    }

    @GetMapping("/insertData/{datasourceName}")
    @Transactional  // spring的事务注解,在本案例中不会失效
    public void insertData(@PathVariable("datasourceName") String datasourceName, @RequestBody Stu stu){
        DataSourceAndyContextHolder.setDataSource(datasourceName);
        stuMapper.insertStu(stu);
        // int i = 3/0; 这一行是为了测试事务,各位大佬可以自行测似
        DataSourceAndyContextHolder.removeDataSource();
    }
}

2.切换数据源代码

1.先来个存储数据源的map

public class DataSourceAndyContextHolder {
    // 此类提供线程局部变量。这些变量不同于它们的正常对应关系是每个线程访问一个线程(通过get、set方法),有自己的独立初始化变量的副本。
    private static final ThreadLocal<String> DATASOURCE_HOLDER = new ThreadLocal<>();

    /**
     * 设置数据源
     * @param dataSourceName 数据源名称
     */
    public static void setDataSource(String dataSourceName){
        DATASOURCE_HOLDER.set(dataSourceName);
    }

    /**
     * 获取当前线程的数据源
     * @return 数据源名称
     */
    public static String getDataSource(){
        return DATASOURCE_HOLDER.get();
    }

    /**
     * 删除当前数据源
     */
    public static void removeDataSource(){
        DATASOURCE_HOLDER.remove();
    }

}

2.实现动态切换数据源

public class DynamicDataAndySource extends AbstractRoutingDataSource {
    public DynamicDataAndySource(DataSource defaultDataSource, Map<Object, Object> targetDataSources){
        super.setDefaultTargetDataSource(defaultDataSource);
        super.setTargetDataSources(targetDataSources);
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceAndyContextHolder.getDataSource();
    }
}

3.设置数据源

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

    @Bean
    @ConfigurationProperties("spring.datasource.druid.db2")
    public DataSource slaveDataSource(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "dynamicDataSource")
    @Primary
    public DynamicDataAndySource createDynamicDataSource(){
        Map<Object,Object> dataSourceMap = new HashMap<>();
        DataSource defaultDataSource = masterDataSource();
        dataSourceMap.put("db1",defaultDataSource);
        dataSourceMap.put("db2",slaveDataSource());
        return new DynamicDataAndySource(defaultDataSource,dataSourceMap);
    }
}

3.第二节代码的测试

1.测试db1库

127.0.0.1:8080/getData/db1

java每日一记 —— mybatis的动态数据源切换_第1张图片

2.测试db2库

127.0.0.1:8080/getData/db2

java每日一记 —— mybatis的动态数据源切换_第2张图片

4.用注解的方式进行优化

1.导入坐标

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.定义一个注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface AndyDS {
    String value() default "db1";
}

3.定义切面

@Aspect
@Component
@Slf4j
public class DSAspect {

    @Pointcut("@annotation(com.andy.config.AndyDS)")
    public void dynamicDataSource(){}

    @Around("dynamicDataSource()")
    public Object datasourceAround(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature)point.getSignature();
        Method method = signature.getMethod();
        AndyDS ds = method.getAnnotation(AndyDS.class);
        if (Objects.nonNull(ds)){
            DataSourceAndyContextHolder.setDataSource(ds.value());
        }
        try {
            return point.proceed();
        } finally {
            DataSourceAndyContextHolder.removeDataSource();
        }
    }
}

4.测试代码

    @GetMapping("/getData_2")
    @AndyDS("db2") 
    public List<Stu> getData_1(){
        List<Stu> all = stuMapper.findAll();
        return all;
    }

5.测试结果

127.0.0.1:8080/getData_2

java每日一记 —— mybatis的动态数据源切换_第3张图片

你可能感兴趣的:(java每日一记,java,mybatis,开发语言)