SpringBoot+MyBatis-Plus实现配置多数据源并动态切换

 更新时间:2026年07月06日 08:51:40   作者:张老师技术栈  
企业级项目中经常需要连接多个数据库——读写分离、分库分表、跨库查询,这一篇讲 SpringBoot + MyBatis-Plus 如何配置多数据源并动态切换,需要的朋友可以参考下

引言

企业级项目中经常需要连接多个数据库——读写分离、分库分表、跨库查询。这一篇讲 SpringBoot + MyBatis-Plus 如何配置多数据源并动态切换。

一、什么时候需要多数据源

读写分离:
  写操作 → master 库(主库)
  读操作 → slave 库(从库)
  好处:分摊主库压力
多库业务:
  用户数据 → user_db
  订单数据 → order_db
  好处:数据隔离,防止单库过大
第三方面对接:
  业务库 → 自己的 MySQL
  对接库 → 第三方的数据库

二、配置多数据源

1. 引入依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.6.1</version>
</dependency>

2. 配置多数据源

spring:
  datasource:
    dynamic:
      # 默认数据源(必须)
      primary: master
      # 是否启用严格模式(找不到数据源抛异常)
      strict: false
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/seckill_db?useUnicode=true&characterEncoding=utf-8
          username: root
          password: 123456
          driver-class-name: com.mysql.cj.jdbc.Driver
        slave:
          url: jdbc:mysql://192.168.1.100:3306/seckill_db?useUnicode=true&characterEncoding=utf-8
          username: root
          password: 123456
          driver-class-name: com.mysql.cj.jdbc.Driver
        order:
          url: jdbc:mysql://localhost:3306/order_db?useUnicode=true&characterEncoding=utf-8
          username: root
          password: 123456
          driver-class-name: com.mysql.cj.jdbc.Driver

不需要自己写配置类,引入依赖后只需要配置 yml 即可。

三、使用 @DS 切换数据源

1. 在 Service 上使用

@Service
@DS("master")  // 类级别:整个类默认走 master
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {

    @Override
    @DS("slave")  // 方法级别:这个方法走 slave
    public List<User> listUsers() {
        return this.list();
    }

    @Override
    @DS("master") // 写操作走 master
    public boolean saveUser(User user) {
        return this.save(user);
    }
}

2. 在 Mapper 上使用

@DS("order")
public interface OrderMapper extends BaseMapper<Order> {
    // 这个 Mapper 的所有操作都走 order 数据源
}

3. 执行原理

@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;    // order 库
    @Autowired
    private UserService userService;    // master 库

    public OrderVO getOrderDetail(Long orderId) {
        // 查订单(自动走 order 数据源)
        Order order = orderMapper.selectById(orderId);

        // 查用户(自动切回 master)
        User user = userService.getById(order.getUserId());

        return merge(order, user);
    }
}

四、读写分离实战

@Service
@DS("master")  // 默认写库
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> {

    @DS("slave")  // 查询走从库
    public List<Product> listProducts() {
        return this.list();
    }

    @DS("slave")
    public Product getById(Long id) {
        return this.getById(id);
    }

    // 增删改不走 @DS,默认走 master
}

读写分离的好处:

单库:所有请求打到一个库
      1000 次查询 + 100 次写入 = 1100 次请求

读写分离:写入走主库,查询走从库
          主库:100 次写入
          从库:1000 次查询
          主库压力小 10 倍

五、多库事务

跨数据源的事务需要手动控制:

@Service
public class MultiDbService {

    @Autowired
    @Lazy
    private MultiDbService self;

    /**
     * 跨库操作:用户库 + 订单库
     * 不能用 @Transactional,需要用编程式事务
     */
    public void createUserAndOrder(User user, Order order) {
        // 使用 TransactionTemplate 控制事务
        TransactionTemplate userTx = new TransactionTemplate();
        TransactionTemplate orderTx = new TransactionTemplate();

        userTx.execute(status -> {
            try {
                // 插入用户(master 库)
                self.saveUser(user);
                // 插入订单(order 库)
                self.saveOrder(order);
                return null;
            } catch (Exception e) {
                status.setRollbackOnly();
                throw e;
            }
        });
    }

    @DS("master")
    public void saveUser(User user) {
        // 保存到用户库
    }

    @DS("order")
    public void saveOrder(Order order) {
        // 保存到订单库
    }
}

六、动态数据源切换原理

/**
 * @DS 注解的原理:
 * 通过 AOP 拦截,在执行前把数据源名称设置到 ThreadLocal 中
 * 执行完后清除
 */
@Aspect
@Component
public class DataSourceAspect {

    @Around("@annotation(ds)")
    public Object around(ProceedingJoinPoint pjp, DS ds) throws Throwable {
        // 切换数据源
        DynamicDataSourceContextHolder.push(ds.value());
        try {
            return pjp.proceed();
        } finally {
            // 使用完后清除
            DynamicDataSourceContextHolder.poll();
        }
    }
}

简化使用: 引入 dynamic-datasource 依赖后,AOP 逻辑已经内置,不需要自己写切面。

七、常见问题

1. 事务中切换失败

@Service
public class UserService {

    @Transactional  // 事务内
    public void doSomething() {
        // 此时数据源已经在事务开始时固定了
        // 内部的 @DS 切换不会生效!
    }
}

解决: 避免在事务中切换数据源,或者把不同数据源的操作拆分到不同 Service。

2. 多数据源 + MyBatis-Plus 分页

// MyBatis-Plus 分页插件在多数据源下仍然有效
// 只需要在配置类中配置一次即可
@Configuration
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

3. 数据源配置加密

spring:
  datasource:
    dynamic:
      datasource:
        master:
          password: ENC(加密后的密码)  # 配合 Jasypt 加密

八、多数据源 vs 分库分表

方案适用场景复杂度
多数据源 @DS不同业务库、读写分离⭐ 简单
分库分表 ShardingSphere单表数据量过大⭐⭐⭐⭐
多数据源 + 分库分表大型项目⭐⭐⭐⭐⭐

建议: 项目初期读写分离用 @DS 完全够用,等数据量上亿后再考虑 ShardingSphere。

到此这篇关于SpringBoot+MyBatis-Plus实现配置多数据源并动态切换的文章就介绍到这了,更多相关SpringBoot MyBatis-Plus多数据源并动态切换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java实现统计在线人数功能的方法详解

    Java实现统计在线人数功能的方法详解

    很多人在笔试或者面试中问到:现在要你实现一个统计在线人数的功能,你该怎么设计?不知道的朋友,这篇文章就来告诉你具体实现方法
    2022-08-08
  • Java中判断素数的五种有效方法

    Java中判断素数的五种有效方法

    本文介绍了五种在Java中判断素数的有效方法,包括从2到x-1测试、排除偶数、仅测试到sqrt(x),以及使用素数表和计算机思维方式的高级策略,文章详细解释了每种方法的时间复杂度和实现细节,需要的朋友可以参考下
    2026-01-01
  • Java 在游戏中探索数组二维数组

    Java 在游戏中探索数组二维数组

    数组和二维数组感觉用王者荣耀的装备栏来举例解释,应该更易懂一些。从基础开始讲,后续会讲到JAVA高级,中间会穿插面试题和项目实战,希望能给大家带来帮助
    2022-03-03
  • 关于Java中的实体类要 implements Serializable的原因分析

    关于Java中的实体类要 implements Serializable的原因分析

    这篇文章主要介绍了Java中的实体类为什么要 implements Serializable,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-06-06
  • mybatis example如何自动生成代码 排序语句

    mybatis example如何自动生成代码 排序语句

    这篇文章主要介绍了mybatis example如何自动生成代码 排序语句,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • JDBC增删改查和查唯一的完整代码解析

    JDBC增删改查和查唯一的完整代码解析

    这篇文章主要介绍了JDBC增删改查和查唯一的完整代码解析,代码分为第四部分,每部分代码都不错,对jdbc增删改查操作感兴趣的朋友一起学习吧
    2016-12-12
  • Spring中Bean命名的方式总结

    Spring中Bean命名的方式总结

    在 Spring 框架中,每个 bean 必须至少有一个唯一的名称,这篇文章主要为大家详细介绍了Spring中Bean命名的各种方式,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-12-12
  • 关于集合中的并发修改异常及处理方式

    关于集合中的并发修改异常及处理方式

    这篇文章主要介绍了关于集合中的并发修改异常及处理方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-06-06
  • Java实现24点小游戏

    Java实现24点小游戏

    这篇文章主要为大家详细介绍了Java实现24点小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-01-01
  • springmvc 分页查询的简单实现示例代码

    springmvc 分页查询的简单实现示例代码

    我们在开发项目中很多项目都用到列表分页功能,本篇介绍了springmvc 分页查询的简单实现示例代码,非常具有实用价值,需要的朋友可以参考下。
    2017-01-01

最新评论