依赖版本
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.2.1</version>
</dependency>
问题1:
异常重现步骤: 1. 独立库A,独立库B,在其中分别建立同名表table1 2. 配置多数据源,分别配置库A与库B 3. 使用@Transactional注解向库B的table1中写入数据
异常表现: 打印结果显示写入成功后,库A与库B中均无数据。
问题2:
异常重现步骤: 1. 在问题1的库A中建立table2_1,库B中建立table2_2,二者同名,但字段不同。 2. table2_2中设定外键,外键引用table1的字段 3. 使用@Transactional注解,向table1与table2_2中依次写入数据
异常表现: 写入table2_2异常,无法找到字段写入失败
以上问题在不加事务时均不会出现。
建表代码:
CREATE TABLE `table1` (
`id` int NOT NULL,
`key` varchar(255) NULL COMMENT '被引用的字段',
`name` varchar(255) NULL COMMENT '当前行名称',
PRIMARY KEY (`id`)
);
CREATE TABLE `table2_1` (
`id` int NOT NULL,
`name` varchar(255) NULL COMMENT '当前行名称',
`fkey` varchar(255) NULL COMMENT '外键',
PRIMARY KEY (`id`),
CONSTRAINT `tablefkey` FOREIGN KEY (`fkey`) REFERENCES `database_a`.`table1` (`key`) ON DELETE RESTRICT ON UPDATE RESTRICT
)
CREATE TABLE `table1` (
`id` int NOT NULL,
`key` varchar(255) NULL COMMENT '被引用的字段',
`name` varchar(255) NULL COMMENT '当前行名称',
PRIMARY KEY (`id`)
);
CREATE TABLE `table2_2` (
`id` int NOT NULL,
`name` varchar(255) NULL COMMENT '当前行名称',
`fkey` varchar(255) NULL COMMENT '外键',
`extra_col` varchar(255) NULL COMMENT '额外字段'
PRIMARY KEY (`id`),
CONSTRAINT `tablefkey` FOREIGN KEY (`fkey`) REFERENCES `database_b`.`table1` (`key`) ON DELETE RESTRICT ON UPDATE RESTRICT
)
Comment From: huayanYu
不支持事务切换
Comment From: WilliamChen-luckbob
你是指只能对Primary标记的库加事务吗?目前这边都是简单的单表操作或者对同一个库中的多个表进行操作,不涉及跨库和事务传递。
不支持事务切换
你是指只能对Primary标记的库加事务吗?目前这边都是简单的单表操作或者对同一个库中的多个表进行操作,不涉及跨库和事务传递。
Comment From: huayanYu
@transactional 下不能切换数据源,理解这句话就对了
Comment From: WilliamChen-luckbob
@transactional 下不能切换数据源,理解这句话就对了
这样子的话是不是出了primary之外的数据源均无法使用事务呢? 我的配置是这样子的:
datasource:
dynamic:
primary: database-a
strict: true
datasource:
database-a:
url: jdbc:mysql://192.168.24.201:3306/database_a?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
database-b:
url: jdbc:mysql://192.168.24.201:3306/database_b?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
接口MpTable2_2Service的实现类由@DS("database-b")标记,直接向table2_2中写入数据,带事务就失败,不带事务的话就正常,是不是因为@Transactional注解实际上是把事务加在datasource-a上了
@SpringBootTest
@RunWith(SpringRunner.class)
@ActiveProfiles("test")
public class DynamicDataSourceTest {
@Resource
private MpTable2_2Service service;
@Test
@Transactional(rollbackFor = Exception.class)
public void savingTest() {
Table2_2 table = new Table2_2();
service.save(table);
}
}
Comment From: WilliamChen-luckbob
@transactional 下不能切换数据源,理解这句话就对了
如此一来是否有办法对非Primary数据源的表加上事务呢?这种场景下两个库是非主从关系的,只是迫不得已要放进同一个服务中。
Comment From: zz630
@transactional 下不能切换数据源,理解这句话就对了
这样子的话是不是出了primary之外的数据源均无法使用事务呢? 我的配置是这样子的:
datasource: dynamic: primary: database-a strict: true datasource: database-a: url: jdbc:mysql://192.168.24.201:3306/database_a?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver database-b: url: jdbc:mysql://192.168.24.201:3306/database_b?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver接口
MpTable2_2Service的实现类由@DS("database-b")标记,直接向table2_2中写入数据,带事务就失败,不带事务的话就正常,是不是因为@transactional注解实际上是把事务加在datasource-a上了``` @SpringBootTest @RunWith(SpringRunner.class) @ActiveProfiles("test") public class DynamicDataSourceTest { @Resource private MpTable2_2Service service;
@Test @Transactional(rollbackFor = Exception.class) public void savingTest() { Table2_2 table = new Table2_2(); service.save(table); }} ```
先开启事务,再切换数据源,变分布式事务了
Comment From: WilliamChen-luckbob
@transactional 下不能切换数据源,理解这句话就对了
这样子的话是不是出了primary之外的数据源均无法使用事务呢? 我的配置是这样子的:
datasource: dynamic: primary: database-a strict: true datasource: database-a: url: jdbc:mysql://192.168.24.201:3306/database_a?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver database-b: url: jdbc:mysql://192.168.24.201:3306/database_b?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver接口
MpTable2_2Service的实现类由@DS("database-b")标记,直接向table2_2中写入数据,带事务就失败,不带事务的话就正常,是不是因为@transactional注解实际上是把事务加在datasource-a上了 ``` @SpringBootTest @RunWith(SpringRunner.class) @ActiveProfiles("test") public class DynamicDataSourceTest { @Resource private MpTable2_2Service service;@Test @Transactional(rollbackFor = Exception.class) public void savingTest() { Table2_2 table = new Table2_2(); service.save(table); }} ```
先开启事务,再切换数据源,变分布式事务了
嗯嗯,这个问题我已经解决了,原因确实是先开了事务。
现在@Transactional与@DS平级或在@DS之后,便不会出现此类问题。
同时我还意识到,如果我使用propagation = Propagation.REQUIRES_NEW传播级别,似乎是可以正确地在事务中开启新事务从而切换数据源的,那么在这个场景中,我试图通过propagation = Propagation.REQUIRES_NEW来执行一些从库的处理。
然而我发现,除了主库的表外,从库均能够正常切换数据源并各自写入成功,而主库仅有日志显式插入行成功,但未能提交。
此时当我单独测试向主库简单插入时,发现单表插入执行成功但事务确实未能提交,不知道是为啥呢,这个是我新提的issue 地址