Spring Boot 3.4.0 结合 Mybatis-plus 实现动态数据源的完整方案

 更新时间:2025年04月01日 10:01:17   作者:Joyous  
本文详细介绍了在 Spring Boot 3.4.0 项目中结合 Mybatis-plus 实现动态数据源切换的完整方案,通过自定义注解和AOP切面,我们可以优雅地实现方法级别的数据源切换,满足多数据源场景下的各种需求,感兴趣的朋友一起看看吧

前言

在实际企业级应用开发中,多数据源的需求非常常见。本文将详细介绍如何在 Spring Boot 3.4.0 项目中结合 Mybatis-plus 实现动态数据源切换功能。

一、环境准备

首先确保你的开发环境满足以下要求:

  • JDK 17+
  • Spring Boot 3.4.0
  • Mybatis-plus 3.5.3.1+
  • Maven 3.6.3+

二、项目配置

1. 添加依赖

pom.xml 中添加以下依赖:

<dependencies>
    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Mybatis-plus -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.3.1</version>
    </dependency>
    <!-- 数据库驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <!-- 其他工具 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

2. 配置文件

application.yml 中配置多个数据源:

spring:
  datasource:
    master:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/master_db?useSSL=false&serverTimezone=UTC
      username: root
      password: 123456
    slave1:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/slave1_db?useSSL=false&serverTimezone=UTC
      username: root
      password: 123456
    slave2:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/slave2_db?useSSL=false&serverTimezone=UTC
      username: root
      password: 123456

三、实现动态数据源

1. 数据源枚举

public enum DataSourceType {
    MASTER("master"),
    SLAVE1("slave1"),
    SLAVE2("slave2");
    private final String name;
    DataSourceType(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}

2. 动态数据源上下文

public class DynamicDataSourceContextHolder {
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
    public static void setDataSourceType(String dataSourceType) {
        CONTEXT_HOLDER.set(dataSourceType);
    }
    public static String getDataSourceType() {
        return CONTEXT_HOLDER.get();
    }
    public static void clearDataSourceType() {
        CONTEXT_HOLDER.remove();
    }
}

3. 动态数据源配置

@Configuration
@MapperScan(basePackages = "com.example.mapper")
public class DynamicDataSourceConfig {
    @Bean
    @ConfigurationProperties("spring.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }
    @Bean
    @ConfigurationProperties("spring.datasource.slave1")
    public DataSource slave1DataSource() {
        return DataSourceBuilder.create().build();
    }
    @Bean
    @ConfigurationProperties("spring.datasource.slave2")
    public DataSource slave2DataSource() {
        return DataSourceBuilder.create().build();
    }
    @Bean
    public DataSource dynamicDataSource() {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.MASTER.getName(), masterDataSource());
        targetDataSources.put(DataSourceType.SLAVE1.getName(), slave1DataSource());
        targetDataSources.put(DataSourceType.SLAVE2.getName(), slave2DataSource());
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
        return dynamicDataSource;
    }
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

4. 动态数据源实现

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}

5. 自定义注解

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

6. AOP 切面实现

@Aspect
@Component
@Order(-1)
@Slf4j
public class DataSourceAspect {
    @Pointcut("@annotation(com.example.annotation.DataSource)" +
            "|| @within(com.example.annotation.DataSource)")
    public void dsPointCut() {
    }
    @Around("dsPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        DataSource dataSource = getDataSource(point);
        if (dataSource != null) {
            DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().getName());
        }
        try {
            return point.proceed();
        } finally {
            DynamicDataSourceContextHolder.clearDataSourceType();
        }
    }
    private DataSource getDataSource(ProceedingJoinPoint point) {
        MethodSignature signature = (MethodSignature) point.getSignature();
        DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
        if (dataSource != null) {
            return dataSource;
        }
        return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
    }
}

四、使用示例

1. Service 层使用

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Override
    @DataSource(DataSourceType.MASTER)
    public User getMasterUser(Long id) {
        return userMapper.selectById(id);
    }
    @Override
    @DataSource(DataSourceType.SLAVE1)
    public User getSlave1User(Long id) {
        return userMapper.selectById(id);
    }
    @Override
    @DataSource(DataSourceType.SLAVE2)
    public User getSlave2User(Long id) {
        return userMapper.selectById(id);
    }
}

2. Controller 层调用

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;
    @GetMapping("/master/{id}")
    public User master(@PathVariable Long id) {
        return userService.getMasterUser(id);
    }
    @GetMapping("/slave1/{id}")
    public User slave1(@PathVariable Long id) {
        return userService.getSlave1User(id);
    }
    @GetMapping("/slave2/{id}")
    public User slave2(@PathVariable Long id) {
        return userService.getSlave2User(id);
    }
}

五、注意事项

  • 事务管理:动态数据源切换与事务管理需要特别注意,建议在事务方法中不要切换数据源
  • 连接池配置:可以为每个数据源单独配置连接池参数
  • 性能考虑:频繁切换数据源可能会影响性能,应根据实际需求合理设计
  • 异常处理:做好数据源切换失败时的异常处理

六、总结

本文详细介绍了在 Spring Boot 3.4.0 项目中结合 Mybatis-plus 实现动态数据源切换的完整方案。通过自定义注解和AOP切面,我们可以优雅地实现方法级别的数据源切换,满足多数据源场景下的各种需求。

到此这篇关于Spring Boot 3.4.0 结合 Mybatis-plus 实现动态数据源的完整方案的文章就介绍到这了,更多相关Spring Boot Mybatis-plus动态数据源内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • MyBatis-Plus注解配置、条件构造器与自定义SQL的复杂操作实例详解

    MyBatis-Plus注解配置、条件构造器与自定义SQL的复杂操作实例详解

    MyBatis-Plus是一个MyBatis的增强工具,提供快速开发单表CRUD操作等功能,通过依赖引入和配置,可以快速创建和使用Mapper接口进行数据库操作,这篇文章通过实例代码介绍MyBatis-Plus注解配置、条件构造器与自定义SQL的复杂操作,感兴趣的朋友一起看看吧
    2026-02-02
  • JavaWeb学习笔记分享(必看篇)

    JavaWeb学习笔记分享(必看篇)

    下面小编就为大家带来一篇JavaWeb学习笔记分享(必看篇)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-06-06
  • 解析和解决org.springframework.beans.factory.NoSuchBeanDefinitionException异常问题

    解析和解决org.springframework.beans.factory.NoSuchBeanDefinitionE

    这篇文章主要介绍了解析和解决org.springframework.beans.factory.NoSuchBeanDefinitionException异常问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-04-04
  • Java 调整格式日志输出

    Java 调整格式日志输出

    本文主要介绍Java 的日志输出格式,在开发java的时候会经常看日志进行调试或者查看错误,这里给大家介绍日志输出调整格式,以便大家看日志的时候更加方便,
    2016-07-07
  • Myeclipse清理项目缓存的几大方法

    Myeclipse清理项目缓存的几大方法

    今天小编就为大家分享一篇关于Myeclipse清理项目缓存的几大方法,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • java 页面url传值中文乱码的解决方法

    java 页面url传值中文乱码的解决方法

    本节讲的是ajax 的URL参数中有中文值,传到服务端,在用request.getParameter()方法,得到的常常会是乱码。
    2013-03-03
  • Spring Boot 表单验证篇

    Spring Boot 表单验证篇

    在表单提交的时候,我们需要进行验证。下面通过本篇文章给大家介绍Spring Boot 表单验证篇,需要的朋友可以参考下
    2017-08-08
  • Java并发编程示例(六):等待线程执行终止

    Java并发编程示例(六):等待线程执行终止

    这篇文章主要介绍了Java并发编程示例(六):等待线程执行终止,在本节,示例程序演示等待初始化方法完成后,再去执行其他任务,需要的朋友可以参考下
    2014-12-12
  • Mybatis核心配置文件加载流程详解

    Mybatis核心配置文件加载流程详解

    本文将介绍MyBatis在配置文件加载的过程中,如何加载核心配置文件、如何解析映射文件中的SQL语句以及每条SQL语句如何与映射接口的方法进行关联,具有一定的参考价值,感兴趣的可以了解一下
    2023-12-12
  • Java利用Optional解决空指针异常

    Java利用Optional解决空指针异常

    这篇文章主要介绍了Java利用Optional解决空指针异常,Optional 类是一个包含有可选值的包装类,这意味着 Optional 类既可以含有对象也可以为空
    2022-09-09

最新评论