tacit/mybatis-plus-multi-datasour...

10 KiB
Raw Blame History

MyBatis Plus 多数据源配置与使用文档

1. 环境与依赖

1.1 环境要求

  • Spring Boot 3.x
  • MyBatis Plus 3.5.x
  • Java 21

1.2 依赖添加

添加 MyBatis Plus 动态数据源依赖:

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

2. 配置文件修改

2.1 主配置文件

配置多数据源:

spring:
   datasource:
      dynamic:
         primary: master
         strict: false
         datasource:
            master:
               driver-class-name: com.mysql.cj.jdbc.Driver
               url: jdbc:mysql://117.72.94.232:53306/tacit?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
               username: root
               password: fU44GFH5
            slave:
               driver-class-name: com.mysql.cj.jdbc.Driver
               url: jdbc:mysql://localhost:3306/tacit?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
               username: root
               password: root123

2.2 配置说明

配置项 说明 示例值
primary 默认数据源名称 master
strict 是否启用严格模式 false
datasource.master 主数据源配置 -
datasource.slave 从数据源配置 -
driver-class-name 数据库驱动类名 com.mysql.cj.jdbc.Driver
url 数据库连接地址 jdbc:mysql://117.72.94.232:53306/tacit
username 数据库用户名 root
password 数据库密码 password

3. 代码实现

3.1 实体类

创建测试实体类 TestEntity

package com.tacit.admin.entity;

import com.baomidou.mybatisplus.annotation.TableName;
import com.tacit.common.entity.Base;
import lombok.Data;
import lombok.EqualsAndHashCode;

@Data
@EqualsAndHashCode(callSuper = true)
@TableName("test")  // 指定数据库表名
public class TestEntity extends Base {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;    // 姓名
    private Integer age;    // 年龄
    private String email;   // 邮箱
}

3.2 Mapper 接口

创建 Mapper 接口 TestMapper

package com.tacit.admin.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.tacit.admin.entity.TestEntity;
import org.apache.ibatis.annotations.Mapper;

@Mapper  // 标记为 MyBatis Mapper 接口
public interface TestMapper extends BaseMapper<TestEntity> {
    // 继承 BaseMapper 后,自动拥有 CRUD 方法
}

3.3 Service 接口

创建 Service 接口 TestService

package com.tacit.admin.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.tacit.admin.entity.TestEntity;

public interface TestService extends IService<TestEntity> {
    /**
     * 根据 ID 查询测试数据
     * @param id 主键 ID
     * @return TestEntity
     */
    TestEntity getTestById(Long id);
}

3.4 Service 实现类

3.4.1 主数据源实现类

创建主数据源 Service 实现类 TestMasterServiceImpl

package com.tacit.admin.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tacit.admin.entity.TestEntity;
import com.tacit.admin.mapper.TestMapper;
import com.tacit.admin.service.TestService;
import org.springframework.stereotype.Service;

@Service("testMasterService")  // 指定 Bean 名称,方便注入
@DS("master")  // 指定使用主数据源
public class TestMasterServiceImpl extends ServiceImpl<TestMapper, TestEntity> implements TestService {
    
    /**
     * 使用主数据源查询数据
     * @param id 主键 ID
     * @return TestEntity
     */
    @Override
    public TestEntity getTestById(Long id) {
        return getById(id);  // 调用父类方法,自动使用主数据源
    }
}

3.4.2 从数据源实现类

创建从数据源 Service 实现类 TestSlaveServiceImpl

package com.tacit.admin.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tacit.admin.entity.TestEntity;
import com.tacit.admin.mapper.TestMapper;
import com.tacit.admin.service.TestService;
import org.springframework.stereotype.Service;

@Service("testSlaveService")  // 指定 Bean 名称,方便注入
@DS("slave")  // 指定使用从数据源
public class TestSlaveServiceImpl extends ServiceImpl<TestMapper, TestEntity> implements TestService {
    
    /**
     * 使用从数据源查询数据
     * @param id 主键 ID
     * @return TestEntity
     */
    @Override
    public TestEntity getTestById(Long id) {
        return getById(id);  // 调用父类方法,自动使用从数据源
    }
}

3.5 控制器

创建测试控制器 MultiDatasourceTestController

package com.tacit.admin.controller;

import com.tacit.common.entity.ResponseResult;
import com.tacit.admin.entity.TestEntity;
import com.tacit.admin.service.TestService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/multi-datasource")
@Tag(name = "多数据源测试接口", description = "用于测试多数据源的接口")
public class MultiDatasourceTestController {

    @Autowired
    @Qualifier("testMasterService")  // 注入主数据源 Service
    private TestService testMasterService;

    @Autowired
    @Qualifier("testSlaveService")  // 注入从数据源 Service
    private TestService testSlaveService;

    @Operation(summary = "使用主数据源创建测试数据", description = "使用主数据源创建测试数据")
    @PostMapping("/master/create")
    public ResponseResult<TestEntity> createTestDataWithMaster(@RequestBody TestEntity testEntity) {
        testMasterService.save(testEntity);  // 使用主数据源保存数据
        return ResponseResult.success(testEntity);
    }

    @Operation(summary = "使用主数据源查询测试数据", description = "使用主数据源查询测试数据")
    @GetMapping("/master/{id}")
    public ResponseResult<TestEntity> getTestDataWithMaster(@PathVariable Long id) {
        return ResponseResult.success(testMasterService.getTestById(id));  // 使用主数据源查询数据
    }

    @Operation(summary = "使用从数据源查询测试数据", description = "使用从数据源查询测试数据")
    @GetMapping("/slave/{id}")
    public ResponseResult<TestEntity> getTestDataWithSlave(@PathVariable Long id) {
        return ResponseResult.success(testSlaveService.getTestById(id));  // 使用从数据源查询数据
    }
}

4. 使用方法

4.1 注解方式

使用 @DS 注解可以指定方法或类使用的数据源:

// 类级别:整个类的方法都使用 master 数据源
@Service
@DS("master")
public class TestServiceImpl {
    // ...
}

// 方法级别:该方法使用 slave 数据源(优先级高于类级别)
@DS("slave")
public TestEntity getTestById(Long id) {
    return getById(id);
}

4.2 动态数据源名称

支持从方法参数或上下文中获取动态数据源名称:

// 从方法参数中获取数据源名称
@DS("#datasourceName")
public TestEntity getTestById(Long id, String datasourceName) {
    return getById(id);
}

// 从上下文或SpEL表达式中获取数据源名称
@DS("#{@systemConfig.getDatasourceName()}")
public TestEntity getTestById(Long id) {
    return getById(id);
}

4.3 数据源优先级

数据源的优先级顺序为:

  1. 方法上的 @DS 注解
  2. 类上的 @DS 注解
  3. 全局默认数据源primary 注意Service 层的 @DS 注解具有更高优先级

5. 测试验证

5.1 编译测试

运行以下命令编译项目,确保配置正确:

cd d:\workSpace\tacit; mvn clean compile -pl tacit-admin -am

5.2 运行测试

启动项目后,通过以下方式测试多数据源:

  1. 使用主数据源创建数据

    POST /multi-datasource/master/create
    Content-Type: application/json
    
    {
      "name": "测试用户",
      "age": 25,
      "email": "test@example.com"
    }
    
  2. 使用主数据源查询数据

    GET /multi-datasource/master/1
    
  3. 使用从数据源查询数据

    GET /multi-datasource/slave/1
    

6. 注意事项

  1. 依赖冲突:确保项目中没有其他数据源相关的依赖冲突,如 spring-boot-starter-jdbc 或其他动态数据源框架。

  2. 数据源名称:数据源名称必须与配置文件中的名称一致,大小写敏感。

  3. 事务管理:多数据源环境下,事务管理需要特别注意,建议使用 @DSTransactional 注解替代 @Transactional 注解。

  4. 严格模式:生产环境建议开启严格模式(strict: true),确保数据源名称正确。

  5. 性能考虑:频繁切换数据源会带来性能开销,建议根据业务场景合理设计数据源使用策略。

  6. 配置中心:如果使用配置中心(如 Nacos可以将数据源配置迁移到配置中心便于动态调整。

7. 常见问题

7.1 数据源切换不生效

  • 检查 @DS 注解是否正确添加到方法或类上
  • 检查数据源名称是否与配置文件一致
  • 检查是否开启了严格模式,以及默认数据源是否配置正确

7.2 事务不生效

  • 使用 @DSTransactional 注解替代 @Transactional 注解
  • 确保事务方法是 public 修饰的
  • 确保事务方法不是通过内部调用的Spring AOP 代理机制)