Spring Boot 项目中使用 Dynamic Datasource 实现多数据源管理

一 、概述

在现代企业级应用开发中,多数据源管理是一个常见的需求。无论是为了数据隔离、读写分离还是业务分库,都需要对多个数据源进行统一管理。本文将详细介绍如何在 Spring Boot 项目中使用 com.baomidou.dynamic.datasource 库来实现多数据源管理。

二、项目准备

1. 添加依赖

首先,在 l pom.xmll 文件中添加多数据源管理所需的依赖:

xml 复制代码
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.5.2</version>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

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

2. 配置多数据源

在 application.yml 文件中配置三个数据源:

yaml 复制代码
spring:
  datasource:
    dynamic:
      primary: master # 设置默认数据源
      strict: false
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/master_db?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
          username: root
          password: password
          driver-class-name: com.mysql.cj.jdbc.Driver
        slave1:
          url: jdbc:mysql://localhost:3306/slave1_db?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
          username: root
          password: password
          driver-class-name: com.mysql.cj.jdbc.Driver
        slave2:
          url: jdbc:mysql://localhost:3306/slave2_db?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
          username: root
          password: password
          driver-class-name: com.mysql.cj.jdbc.Driver

三、核心注解介绍

1. DS 注解

根据你提供的代码,DS 注解是动态数据源的核心注解:

java 复制代码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DS {
    String value();
}
  • @Target({ElementType.TYPE, ElementType.METHOD}):表示该注解可以作用于类和方法
  • @Retention(RetentionPolicy.RUNTIME):表示该注解在运行时有效
  • String value():指定数据源的名称

四、实现多数据源管理

1. 创建实体类

java 复制代码
// User.java
package com.example.entity;

public class User {
    private Long id;
    private String name;
    private String email;
    
    // 构造函数
    public User() {}
    
    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }
    
    // Getter 和 Setter
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

2. 创建数据访问层

java 复制代码
// UserMapper.java
package com.example.mapper;

import com.example.entity.User;
import org.apache.ibatis.annotations.*;
import java.util.List;

@Mapper
public interface UserMapper {
    
    @Select("SELECT * FROM users")
    List<User> findAll();
    
    @Insert("INSERT INTO users(name, email) VALUES(#{name}, #{email})")
    int insert(User user);
    
    @Update("UPDATE users SET name=#{name}, email=#{email} WHERE id=#{id}")
    int update(User user);
    
    @Delete("DELETE FROM users WHERE id=#{id}")
    int delete(Long id);
}

3. 创建服务层 - 使用不同数据源

java 复制代码
// UserService.java
package com.example.service;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.example.entity.User;
import com.example.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    // 使用主数据源
    @DS("master")
    public List<User> getMasterUsers() {
        System.out.println("从主数据源查询用户");
        return userMapper.findAll();
    }
    
    // 使用从数据源1
    @DS("slave1")
    public List<User> getSlave1Users() {
        System.out.println("从从数据源1查询用户");
        return userMapper.findAll();
    }
    
    // 使用从数据源2
    @DS("slave2")
    public List<User> getSlave2Users() {
        System.out.println("从从数据源2查询用户");
        return userMapper.findAll();
    }
    
    // 在类级别指定数据源
    @DS("master")
    public void addUserToMaster(User user) {
        userMapper.insert(user);
    }
    
    // 方法级别覆盖类级别的数据源设置
    @DS("slave1")
    public void addUserToSlave1(User user) {
        userMapper.insert(user);
    }
}

4. 创建控制器

java 复制代码
// UserController.java
package com.example.controller;

import com.example.entity.User;
import com.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;

@RestController
@RequestMapping("/users")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    @GetMapping("/master")
    public List<User> getMasterUsers() {
        return userService.getMasterUsers();
    }
    
    @GetMapping("/slave1")
    public List<User> getSlave1Users() {
        return userService.getSlave1Users();
    }
    
    @GetMapping("/slave2")
    public List<User> getSlave2Users() {
        return userService.getSlave2Users();
    }
    
    @PostMapping("/master")
    public String addMasterUser(@RequestBody User user) {
        userService.addUserToMaster(user);
        return "用户已添加到主数据源";
    }
    
    @PostMapping("/slave1")
    public String addSlave1User(@RequestBody User user) {
        userService.addUserToSlave1(user);
        return "用户已添加到从数据源1";
    }
}

五、高级用法

1. 在Service类级别使用DS注解

java 复制代码
@Service
@DS("master")  // 整个类都使用master数据源
public class MasterUserService {
    
    @Autowired
    private UserMapper userMapper;
    
    // 这个方法会使用master数据源
    public List<User> getAllUsers() {
        return userMapper.findAll();
    }
    
    // 这个方法会覆盖类级别的设置,使用slave1数据源
    @DS("slave1")  
    public void saveToSlave1(User user) {
        userMapper.insert(user);
    }
}

2. 使用SpEL表达式

java 复制代码
@Service
public class DynamicUserService {
    
    @Autowired
    private UserMapper userMapper;
    
    // 使用SpEL表达式动态选择数据源
    @DS("#user.type")  // 根据user对象的type属性选择数据源
    public void saveUserByType(User user) {
        userMapper.insert(user);
    }
    
    // 根据方法参数动态选择数据源
    @DS("#dsName")
    public List<User> getUsersByDataSource(String dsName) {
        return userMapper.findAll();
    }
}

3. 在Mapper类级别使用DS注解

java 复制代码
// 整个类都使用master数据源
@Mapper
@DS("master")
public interface UserMapper extends BaseMapper<User> {

}

六、数据库表结构

为演示效果,创建三个数据库的用户表:

sql 复制代码
-- master_db
CREATE TABLE users (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50),
    email VARCHAR(100)
);

-- slave1_db
CREATE TABLE users (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50),
    email VARCHAR(100)
);

-- slave2_db
CREATE TABLE users (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50),
    email VARCHAR(100)
);

七、测试验证

启动应用后,可以通过以下API进行测试:

  • GET /users/master - 从主数据源获取用户
  • GET /users/slave1 - 从从数据源1获取用户
  • GET /users/slave2 - 从从数据源2获取用户
  • POST /users/master - 向主数据源添加用户
  • POST /users/slave1 - 向从数据源1添加用户

八、总结

通过使用 com.baomidou.dynamic.datasource 库,我们可以非常方便地在 Spring Boot 项目中实现多数据源管理。主要优点包括:

  • 简单易用 :通过 @DS 注解即可切换数据源
  • 灵活配置:支持方法级和类级注解
  • 动态切换:支持基于表达式的动态数据源选择
  • 性能良好:底层基于 Spring 的事务管理,性能优秀

这种方案特别适合需要读写分离、多租户、分库分表等场景,是企业级应用开发中的重要技术方案。

相关推荐
深入云栈几秒前
Netty 4.2.x 源码深度解析 (十九):ChannelPool 连接池 —— 连接复用与资源管理
java·后端·架构
yunwei371 分钟前
eBPF 教程:BPF 迭代器用于内核数据导出
linux·后端·性能优化
silvamux_28 分钟前
Spring Boot 调用大模型 api 最小可跑教程
java·spring boot·后端
今年下半年30 分钟前
【Spring Boot 】通过 OpenFeign 请求第三方接口
java·spring boot·后端·openfeign
三翼鸟数字化技术团队35 分钟前
Springboot + WebFlux + Disruptor 实现数据转发
java·spring boot
xiaogai_gai36 分钟前
生产汇报单查询同步实战:从金蝶云星空拉取到轻易云数据集成平台的工程化做法
android
UrbanJazzerati1 小时前
AutoHotkey v2 零基础快速入门 Part 3 像素亮度检测完全指南
后端
浮链序1 小时前
Shopify 把 10 亿商品开放给任意 agent 了:我用零依赖 Python 撸了个带预算闸门的代购网关
后端·代码规范
Qlly1 小时前
全栈工程师怎么接需求?从一句「加个功能」聊到能开发、能验收
前端·后端
马可家的菠萝1 小时前
你的微信“文件传输助手”,是不是已经成了第二个网盘?
前端·后端·架构