多数据源与事务

SpringBoot下多数据源的使用以及对事务注解的兼容。

多数据源配置

https://blog.csdn.net/qq_34697930/article/details/135508744

1. 多数据源配置

在实际开发中,经常可能遇到在一个应用中可能需要访问多个数据库的情况,微服务版本采用了dynamic-datasource动态多数据源组件,使用参考:

1、对应模块pom加入cloudsea-common-datasource依赖

1
2
3
4
<dependency>
<groupId>com.cloudsea</groupId>
<artifactId>cloudsea-common-datasource</artifactId>
</dependency>

依赖于

1
2
3
4
5
6
<!--baomidou动态数据源-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.2.1</version>
</dependency>

2、配置主从数据库,在datasource下面加一个master(本来是不用的):

1
2
3
4
5
6
7
spring:
# 配置mysql连接数据源
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://8.146.199.130:3306/otaru?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
username: root
password: Why185999
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
spring:
# 配置mysql连接数据源
datasource:
dynamic:
datasource:
master:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://8.146.199.130:3306/otaru?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
username: root
password: Why185999
# slave:
# driver-class-name: com.mysql.cj.jdbc.Driver
# url: jdbc:mysql://8.146.199.130:3306/ry-vue?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
# username: root
# password: Why185999

3、cloudsea-common-datasource定义数据源注解,对应datasource配置的不同数据源节点

项目默认了主MasterSlave注解可以直接使用,其他的可以根据项目实际情况去添加。

4、使用注解在需要切换数据源的方法上或类上。

1
2
3
4
5
6
7
8
9
10
11
12
@Master
public void insertA()
{
return xxxxMapper.insertXxxx();
}

@Slave
public void insertB()
{
return xxxxMapper.insertXxxx();
}

2. @Transactionl注解

多种形式的事务使用:

https://blog.csdn.net/qq_52524736/article/details/138657889

3. RMS实际操作

3.1. Nacos配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# spring配置
spring:
datasource:
dynamic:
primary: master
datasource:
# 主库数据源
master:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.147.54:3306/rmsdb?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&noAccessToProcedureBodies=true
username: root
password: root
# 从库数据源
slave:
enabled: true
username: postgres
password: 123456
url: jdbc:postgresql://192.168.147.54:5432/rmsdb?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&useSSL=false&allowMultiQueries=true&currentSchema=public
driver-class-name: org.postgresql.Driver
# seata: true # 开启seata代理,开启后默认每个数据源都代理,如果某个不需要代理可单独关闭

3.2. 使用注解@DS

使用@DS(“slave”)注解切换数据源

@DS注解加到mapper接口、service接口、service方法里都不生效,应该添加到service实现类或者实现类里具体的方法上。

3.3. 使用注解@Slave

成功条件:

  1. 主方法上不加事务注解
  2. 小方法中新建事务注解,跳出事务注解(原因是默认的也会开启一个事务注解,我们也需要跳出)
    1
    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
  3. 在mapper的方法上添加@Slave注解
  4. 如果还是不行的话,试试在nacos配置文件中,设置primary库

3.4. @DSTransactional注解

https://www.5axxw.com/questions/simple/kbbuz9

https://www.cnblogs.com/zhaobo1997/p/17976348

如果方法内部需要切换数据源,直接将最外层的注解@Transactional切换为@DSTransactional

3.5. 手动切换数据源

https://blog.csdn.net/u012383839/article/details/136919569

1
2
3
4
5
6
7
8
try {
DynamicDataSourceContextHolder.push("slave");//手动切换
pgReturnObj = resSpecMapper.selectPGGeom(pgParamObj);
} catch (Exception e) {
log.info("pg库数据源切换失败:" + e.getMessage());
}finally {
DynamicDataSourceContextHolder.clear();
}

3.6. 手动切换数据源并查看数据源名称

这种查询的方法只能看到这种手动push的数据源的名称

1
2
3
4
5
6
7
@GetMapping("/dataSourceName")
public void dataSourceName() {
DynamicDataSourceContextHolder.push("master");
log.info("dataSourceName1:{}", DynamicDataSourceContextHolder.peek()); // master
DynamicDataSourceContextHolder.clear();
log.info("dataSourceName2:{}", DynamicDataSourceContextHolder.peek()); // null
}

3.7. 事务与矛盾

https://www.cnblogs.com/sun-10387834/p/16528339.html

想要无伤通关有两种情况:

  1. 取消主方法中上的@Transactional注解
  2. 如果主方法名字是insert或者create等名字,可能会默认实现了事务,那么我建议将多数据源操作放在一个方法中。然后在这个方法上添加新事务注解:
    1
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)

4. 解决方案

4.1. 外事务+mapper-slave注解

https://blog.csdn.net/csdn570566705/article/details/132738498

Service主方法:

1
2
3
4
@DSTransactional
public void main(){
addStationGeom();
}

Service内部方法无操作:

1
2
3
4
5
6
public int addStationGeom(SpcStation spcStation) {

spcStationMapper.addStationGeom(paramObj);

return 1;
}

Mapper方法:

1
2
@Slave
void addStationGeom(JSONObject paramObj);

亲测事务有效,在整个方法的数据库方法结束之前,不会有sql操作?那会不会影响回查?

实际上不影响回查,虽然在表里看不见,但是不影响我们的查询操作。


多数据源与事务
http://wahoyu.xyz/2024/09/10/多数据源与事务/
作者
Wahoyu
发布于
2024年9月10日
许可协议