springboot整合多数据源的配置以及动态切换数据源,注解切换数据源

在许多应用程序中,可能需要使用多个数据库或数据源来处理不同的业务需求。Spring Boot提供了简便的方式来配置和使用多数据源,使开发人员能够轻松处理多个数据库连接。如果你的项目中可能需要随时切换数据源的话,那我这篇文章可能能帮助到你

ℹ️:这里对于pom文件中坐标的引入我就不多赘言了

配置文件

1️⃣:properties文件中

# 数据源配置
spring.datasource.mysql.primary.url=jdbc:mysql://127.0.0.1:3351/tally_book?characterEncoding=utf8&serverTimezone=UTC
spring.datasource.mysql.primary.username=root
spring.datasource.mysql.primary.password=123456
spring.datasource.mysql.primary.driver-class-name=com.mysql.cj.jdbc.Driver

# 数据源配置
spring.datasource.mysql.slave1.url=jdbc:mysql://127.0.0.1:3351/dingding_mid?characterEncoding=utf8&serverTimezone=UTC
spring.datasource.mysql.slave1.username=root
spring.datasource.mysql.slave1.password=123456
spring.datasource.mysql.slave1.driver-class-name=com.mysql.cj.jdbc.Driver

上面的配置文件中我只写了两个源,而且都是mysql 的,primary和slave1就是区分

2️⃣:配置类实现多数据源配置

package com.todoitbo.tallybookdasmart.config;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.todoitbo.tallybookdasmart.multiDataSource.DataSourceType;
import com.todoitbo.tallybookdasmart.multiDataSource.DynamicDataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.*;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

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

/**
 * @author xiaobo
 * @date 2023/5/19
 */
@Configuration
@Slf4j
public class MultiDataSourceConfig {

    @Bean
    public PlatformTransactionManager platformTransactionManager(DataSource dynamicDataSource) {
        return new DataSourceTransactionManager(dynamicDataSource);
    }

    @Bean
    @Primary
    @DependsOn("primaryDataSource")
    public DataSource dynamicDataSource(@Qualifier(DataSourceType.PRIMARY) DataSource primaryDataSource,
                                        @Qualifier(DataSourceType.SECOND) DataSource secondDataSource) {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();

        // 1.设置默认数据源
        dynamicDataSource.setDefaultTargetDataSource(primaryDataSource);
        // 2.配置多数据源
        Map<Object, Object> map = new HashMap<>();
        map.put(DataSourceType.PRIMARY, primaryDataSource);
        map.put(DataSourceType.SECOND, secondDataSource);
        // 3.存放数据源集
        dynamicDataSource.setTargetDataSources(map);
        return dynamicDataSource;
    }

    @Bean(name = DataSourceType.PRIMARY)
    @ConfigurationProperties(prefix = "spring.datasource.mysql.primary")
    public DataSource primaryDataSource() {
        log.info("主数据库连接池创建中.......");
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = DataSourceType.SECOND)
    @ConfigurationProperties(prefix = "spring.datasource.mysql.slave1")
    public DataSource secondDataSource() {
        log.info("second数据库连接池创建中.......");
        return DruidDataSourceBuilder.create().build();
    }

}

3️⃣:自定义注解实现,可使用自定义注解来切换数据源

package com.todoitbo.tallybookdasmart.multiDataSource;

import java.lang.annotation.*;

/**
 * description: 自定义注解,标记数据源
 *
 * @author bo
 * @version 1.0
 * @date 2023/5/19 08:45
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface DataSource {
    String value() default DataSourceType.PRIMARY;
}

4️⃣:定义一个切面类

这段代码是一个切面类DataSourceAspect,用于在方法调用前后切换数据源。以下是代码的解释:

  1. @Aspect:指定该类为切面类,用于定义切面的切入点和增强逻辑。
  2. @Order(value=1):指定切面的执行顺序,数值越小优先级越高。
  3. @Component:将该切面类声明为Spring的组件,使其可以被自动扫描并装配到Spring容器中。
  4. @Pointcut(value = "execution(* com.todoitbo.tallybookdasmart.service.*.*(..)) || execution(* com.todoitbo.tallybookdasmart.*.*(..))"):定义切入点表达式,指定需要切入的目标方法。
  5. @Around("dataSourcePointCut()"):定义环绕通知,表示在目标方法执行前后执行切面逻辑。
  6. public Object around(ProceedingJoinPoint joinPoint) throws Throwable:环绕通知方法,包含切面逻辑。
  7. 在方法中通过反射获取目标方法的注解信息,判断是否存在@DataSource注解,并获取注解中设置的数据源名称。
  8. 调用DataSourceContextHolder.setDataSource(dataSource)方法,将获取到的数据源名称设置到当前线程的上下文中。
  9. 调用joinPoint.proceed()方法,继续执行目标方法。
  10. finally块中调用DataSourceContextHolder.clearDataSourceType()方法,清除当前线程中存储的数据源信息。
package com.todoitbo.tallybookdasmart.multiDataSource;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;


/**
 * @author xiaobo
 */
@Aspect
@Order(value=1)
@Component
@Slf4j
public class DataSourceAspect {

    /** 定义切入点表达式*/
    @Pointcut(value = "execution(* com.todoitbo.tallybookdasmart.service.*.*(..)) || execution(* com.todoitbo.tallybookdasmart.*.*(..))")
    public void dataSourcePointCut() {
    }
    @Around("dataSourcePointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Object target = joinPoint.getTarget();
        String method = joinPoint.getSignature().getName();
        Class<?> classz = target.getClass();
        Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterTypes();
        try {
            // 使用反射获取目标类中指定方法名和参数类型的方法对象
            Method m = classz.getMethod(method, parameterTypes);
            // 设置默认的数据源名称
            String dataSource = DataSourceType.PRIMARY;
            // 判断方法是否被@DataSource注解标记。
            if (m.isAnnotationPresent(DataSource.class)) {
                // 通过getAnnotation()方法获取方法上的@DataSource注解对象。
                DataSource ds = m.getAnnotation(DataSource.class);
                // 获取注解对象中设置的数据源名称
                dataSource = ds.value();
            }
            // 将获取到的数据源名称设置到当前线程的上下文中
            DataSourceContextHolder.setDataSource(dataSource);
            // 继续执行目标方法
            return joinPoint.proceed();
        } finally {
            DataSourceContextHolder.clearDataSourceType();
        }
    }

}

5️⃣:存储和获取当前线程数据源的上下文工具类

这段代码是一个用于存储和获取当前线程数据源的上下文工具类。它使用了Netty的FastThreadLocal来实现线程本地的快速存取。

  1. 创建FastThreadLocal对象:在类中定义了一个名为CONTEXT_HOLDERFastThreadLocal对象,用于存储当前线程的数据源信息。
  2. 设置数据源:setDataSource方法用于将数据源名称设置到当前线程的上下文中。通过调用CONTEXT_HOLDER.set(dataSource),将数据源名称存储在当前线程中。
  3. 获取数据源:getDataSource方法用于从当前线程的上下文中获取数据源名称。通过调用CONTEXT_HOLDER.get(),可以获取当前线程的数据源名称。
  4. 清除数据源:clearDataSourceType方法用于清除当前线程中存储的数据源信息。通过调用CONTEXT_HOLDER.remove(),可以清除当前线程中的数据源信息。
package com.todoitbo.tallybookdasmart.multiDataSource;

import io.netty.util.concurrent.FastThreadLocal;

/**
 * description: 存储和获取当前线程数据源的上下文工具类
 *
 * @author bo
 * @version 1.0
 * @date 2023/5/19 08:44
 */
public class DataSourceContextHolder {

    /**
     * 创建FastThreadLocal对象,存储当前线程的数据源信息
     */
    private static final FastThreadLocal<String> CONTEXT_HOLDER = new FastThreadLocal<String>();

    /**
     * 设置数据源
     */
    public static void setDataSource(String dataSource) {
        CONTEXT_HOLDER.set(dataSource);
    }

    /**
     * 获取数据源
     */
    public static String getDataSource() {
        return CONTEXT_HOLDER.get();
    }

    /**
     * 清除数据源
     */
    public static void clearDataSourceType() {
        CONTEXT_HOLDER.remove();
    }

}

6️⃣:数据源类型

package com.todoitbo.tallybookdasmart.multiDataSource;


/**
 * @author xiaobo
 */
public class DataSourceType {
    public static final String PRIMARY = "primaryDataSource";
    public static final String SECOND = "secondDataSource";
}

7️⃣:根据当前线程中的数据源上下文获取对应的数据源

package com.todoitbo.tallybookdasmart.multiDataSource;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * description: 根据当前线程中的数据源上下文获取对应的数据源。
 *
 * @author bo
 * @version 1.0
 * @date 2023/5/19 08:46
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

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

}

具体实现

在service的实现类的方法上加入注解即可

package com.todoitbo.tallybookdasmart.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.todoitbo.tallybookdasmart.entity.TbConfig;
import com.todoitbo.tallybookdasmart.mapper.TbConfigMapper;
import com.todoitbo.tallybookdasmart.multiDataSource.DataSource;
import com.todoitbo.tallybookdasmart.multiDataSource.DataSourceType;
import com.todoitbo.tallybookdasmart.service.ITbConfigService;
import com.todoitbo.tallybookdasmart.service.base.BaseServiceImpl;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;


/**
 * (TbConfig)服务
 *
 * @author bo
 * @since 2023-04-18 21:13:14
 */
@Service
public class TbConfigServiceImpl extends BaseServiceImpl<TbConfigMapper,TbConfig> implements ITbConfigService {

    @Resource
    protected TbConfigMapper mapper;

    @Override
    @DataSource(DataSourceType.SECOND)
    public List<TbConfig> testList() {
        return mapper.selectList(new QueryWrapper<>());
    }
}

效果图:

image-20230522084706414

⚠️:这里只是想展示他确实是走了从数据源了

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring Boot 是一个用于快速构建 Java 应用程序的框架。它可以与多种其他框架和组件进行整合,以实现更丰富的功能。在这里,我们将讨论如何使用 Spring Boot 整合 Druid、MyBatis、JTA 分布式事务以及多数据源,同时使用 AOP 注解实现动态切换。 首先,我们可以在 Spring Boot 中集成 Druid 数据源。Druid 是一个高性能的 JDBC 连接池,可以提供监控和统计功能。我们可以通过在 pom.xml 文件中添加相关的依赖,并在 application.properties 文件中配置数据源信息,来实现 Druid 的集成。 接下来,我们可以整合 MyBatis 框架,它是一种优秀的持久化解决方案。我们可以使用 MyBatis 来操作数据库,并将其与 Druid 数据源进行整合。为此,我们需要在 pom.xml 文件中添加 MyBatis 和 MyBatis-Spring 的依赖,并配置 MyBatis 的相关配置文件。 此外,我们还可以使用 JTA(Java Transaction API)实现分布式事务。JTA 可以在分布式环境中协调多个参与者的事务操作。我们可以在 pom.xml 文件中添加 JTA 的依赖,并在 Spring Boot配置文件中配置 JTA 的相关属性,以实现分布式事务的支持。 在实现多数据源时,我们可以使用 Spring Boot 的 AbstractRoutingDataSource 来实现动态切换数据源。这个类可以根据当前线程或其他条件选择不同的数据源来进行数据操作。我们可以通过继承 AbstractRoutingDataSource 并实现 determineCurrentLookupKey() 方法来指定当前数据源的 key。然后,在配置文件中配置多个数据源,并将数据源注入到 AbstractRoutingDataSource 中,从而实现动态切换。 最后,我们可以使用 AOP(Aspect Oriented Programming)注解来实现动态切换。AOP 是一种编程范式,可以通过在代码中插入特定的切面(Aspect)来实现横切关注点的处理。我们可以在代码中使用注解来标记需要切换数据源的方法,然后使用 AOP 技术来拦截这些方法,并根据注解中指定的数据源信息来进行数据源切换。 综上所述,通过整合 Druid、MyBatis、JTA 分布式事务以及多数据源,并使用 AOP 注解实现动态切换,我们可以在 Spring Boot 中实现强大而灵活的应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只牛博

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值