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

337 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# MyBatis Plus 多数据源配置与使用文档
## 1. 环境与依赖
### 1.1 环境要求
- Spring Boot 3.x
- MyBatis Plus 3.5.x
- Java 21
### 1.2 依赖添加
添加 MyBatis Plus 动态数据源依赖:
```xml
<!-- Dynamic Datasource -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.6.1</version>
</dependency>
```
## 2. 配置文件修改
### 2.1 主配置文件
配置多数据源:
```yaml
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`
```java
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`
```java
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`
```java
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`
```java
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`
```java
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`
```java
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` 注解可以指定方法或类使用的数据源:
```java
// 类级别:整个类的方法都使用 master 数据源
@Service
@DS("master")
public class TestServiceImpl {
// ...
}
// 方法级别:该方法使用 slave 数据源(优先级高于类级别)
@DS("slave")
public TestEntity getTestById(Long id) {
return getById(id);
}
```
### 4.2 动态数据源名称
支持从方法参数或上下文中获取动态数据源名称:
```java
// 从方法参数中获取数据源名称
@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 编译测试
运行以下命令编译项目,确保配置正确:
```bash
cd d:\workSpace\tacit; mvn clean compile -pl tacit-admin -am
```
### 5.2 运行测试
启动项目后,通过以下方式测试多数据源:
1. **使用主数据源创建数据**
```bash
POST /multi-datasource/master/create
Content-Type: application/json
{
"name": "测试用户",
"age": 25,
"email": "test@example.com"
}
```
2. **使用主数据源查询数据**
```bash
GET /multi-datasource/master/1
```
3. **使用从数据源查询数据**
```bash
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 代理机制)