mybatis多数据源切换-AOP实现

本文介绍了如何使用AOP和注解在Spring Boot项目中实现Mybatis数据源的动态切换,包括配置pom.xml、yml文件,创建数据源配置类、切换类和AOP切面,详细展示了如何通过注解标记方法来自动切换数据源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

方案

  1. AOP+注解:适用于方法级的数据源动态切换
  2. 分包法(生成多个SqlSessionFactory,分别指定不同的mapper包)
    这里我先介绍常用的方式:AOP+注解

代码

1.配置相关

pom.xml

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.6</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.24</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

yml文件

mybatis:
  type-aliases-package: com.jenkins.domain
  mapper-locations: classpath*:/mapper/*.xml

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    dbs:
      db1:
        url: jdbc:mysql://ip:3306/db1?useSSL=false&serverTimezone=UTC
        username: root
        password: admin
        driver-class-name: com.mysql.cj.jdbc.Driver
      db2:
        url: jdbc:mysql://ip:3306/db2?useSSL=false&serverTimezone=UTC
        username: root
        password: admin
        driver-class-name: com.mysql.cj.jdbc.Driver

yml对应配置类

@Data
@Configuration
@ConfigurationProperties(prefix = "spring.datasource")
public class DbConfig {
    private Map<String, DruidDataSource> dbs;
}

数据源配置类

@Configuration
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class DynamicDataSourceConfig {

    private final DbConfig dbConfig;

    @Bean("dynamicDataSource")
    public DynamicDataSource dynamicDataSource() {
        Map<Object, Object> dataSourceMap = new HashMap<>();
        dbConfig.getDbs().forEach(dataSourceMap::put);
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setTargetDataSources(dataSourceMap);
        dynamicDataSource.setDefaultTargetDataSource(dbConfig.getDbs().get("db1"));
        return dynamicDataSource;
    }

}

数据源切换类

public class DynamicDataSourceContextHolder {
	 public static final String DS1_KEY = "db1";
     public static final String DS2_KEY = "db2";
    /**
     * 动态数据源名称上下文,每个线程互不影响
     */
    private static final ThreadLocal<String> DATASOURCE_CONTEXT_KEY = new ThreadLocal<>();

    /**
     * 设置/切换数据源
     */
    public static void setContextKey(String key) {
        DATASOURCE_CONTEXT_KEY.set(key);
    }

    /**
     * 获取数据源名称
     */
    public static String getContextKey() {
        String key = DATASOURCE_CONTEXT_KEY.get();
        return key == null ? DS1_KEY : key;
    }

    /**
     * 删除当前数据源名称
     */
    public static void removeContextKey() {
        DATASOURCE_CONTEXT_KEY.remove();
    }
}

动态数据源类(重点)

public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getContextKey();
    }
}

2.aop

注解

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    String value();
}

切面

@Aspect
@Component
public class DataSourceAspect {

    @Pointcut("@annotation(com.hui.annotations.DataSource)")
    public void pointCut() {
    }


    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        String dsKey = getDSAnnotation(joinPoint).value();
        //切换数据源
        DynamicDataSourceContextHolder.setContextKey(dsKey);
        try {
            return joinPoint.proceed();
        } finally {
            DynamicDataSourceContextHolder.removeContextKey();
        }
    }

    /**
     * 根据类或方法获取数据源注解
     */
    private DataSource getDSAnnotation(ProceedingJoinPoint joinPoint) {
        //获取被代理的对象
        Class<?> targetClass = joinPoint.getTarget().getClass();
        DataSource dsAnnotation = targetClass.getAnnotation(DataSource.class);
        // 先判断类上是否有该注解,再判断方法上是否有注解
        if (Optional.ofNullable(dsAnnotation).isPresent()) {
            return dsAnnotation;
        } else {
            //获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            return methodSignature.getMethod().getAnnotation(DataSource.class);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值