多数据源(动态数据源)的需求:
- 不同的业务分多个数据库场景,例如一个程序负责n个省份的db操作
- 一主多从的读写分离的场景(一主多从可以使用MyBatis插件的方式实现)
dynamic-datasource
dynamic-datasource
是一个开源的 Spring Boot 多数据源启动器,提供了丰富的功能,包括数据源分组、敏感信息加密、独立初始化表结构等。
以下步骤在SpringBoot项目基础上实现:
1. 引入依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
2. 通过yml配置数据源:
默认数据源名为 master
,可通过 spring.datasource.dynamic.primary
修改。
spring:
datasource:
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候回抛出异常,不启动会使用默认数据源.
datasource:
master:
url: jdbc:mysql://xxxxa:3449/db1?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=utf8&useSSL=false&&serverTimezone=Asia/Shanghai&&zeroDateTimeBehavior=convertToNull
username: 用户名
password: 密码
driver-class-name: com.mysql.cj.jdbc.Driver
slave_1:
url: jdbc:mysql://xxxxb:3449/db2?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=utf8&useSSL=false&&serverTimezone=Asia/Shanghai&&zeroDateTimeBehavior=convertToNull
username: 用户名
password: 密码
driver-class-name: com.mysql.cj.jdbc.Driver
3. 使用 @DS
切换数据源:
使用 @DS
注解来指定需要切换到的数据源,DS内部填写的值就是yml里面配置的key。
service层里面在想要切换数据源的方法上加上@DS注解就行了,也可以加在整个service层上,方法上的注解优先于类上注解。
注解 | 结果 |
---|---|
没有@DS | 默认数据源 |
@DS(“dsName”) | dsName可以为组名也可以为具体某个库的名称;数据源名称即yml里面配置的key |
本案例读取
master
数据库中的数据,写入到slave_1
数据库的新表中。
实体类上添加注解@DS("slave_1")
指明当前类所使用的数据库。
@Data
@Accessors(chain = true)
@TableName("T_STUDENT")
@ApiModel(value="T_STUDENT", description="学生表")
@DS("slave_1")
public class StudentVerified {
@ApiModelProperty(value = "主键")
@TableField("id")
private Long id;
@ApiModelProperty(value = "姓名")
@TableField("username")
private String username;
@ApiModelProperty(value = "身份证号")
@TableField("idCardNumber")
private String idCardNumber;
}
如果要使用mybatisplus
方法,需直接在Service类上直接添加注解
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@DS("slave_1")
public class StudentVerifiedServiceImpl extends ServiceImpl<StudentVerifiedMapper, StudentVerified> {
}
public interface StudentVerifiedMapper extends BaseMapper<StudentVerified> {
}
使用master主数据源(不需做任何操作,和之前单数据库一样)
启动项目时可以看到相关日志输出
4. 在测试方法中实现逻辑
@SpringBootTest
public class Multidata {
@Autowired
private IBusCertificationService certificationService;
@Autowired
private StudentVerifiedServiceImpl studentVerifiedService;
/**
* 将master的实名认证数据导入到slave的Student表中
*/
@Test
void mult(){
List<BusCertification> userCertification = certificationService.list();
List<StudentVerified> studentVerifiedList = new ArrayList<>();
for (BusCertification busCertification : userCertification) {
StudentVerified studentVerified = new StudentVerified();
studentVerified.setId(busCertification.getId());
studentVerified.setUsername(busCertification.getUsername());
studentVerified.setIdCardNumber(busCertification.getIdCardNumber());
studentVerifiedList.add(studentVerified);
}
//此处调用方法时会根据注解切换到slave数据库中
studentVerifiedService.saveBatch(studentVerifiedList);
}
}
多数据源项目使用@DS切换数据库在事务中失效问题
如果在同一个方法内分别操作了主从库,并且方法上有事务注解,很有可能会导致从库查询失败。因为加入@Transitional
注解后,数据源切换会失效,只会操作主库。
此时需将方法上注解修改为
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void applyCheck(ApplyCheckPO po) {
//······
//调用从库方法
}
同时,最好将从库相关操作放置从库serviceImpl的方法中,再由外层调用该方法并try…catch环绕,同时从库该方法上也加上注解
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)