SpringBoot 使用多SqlSessionFactory下的事务问题

2023-11-22 17:44

本文主要是介绍SpringBoot 使用多SqlSessionFactory下的事务问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

如下配置了两个数据源:

spring:datasource:ds1:jdbc-url: jdbc:mysql://localhost:3307/spring-boot-demos?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=trueusername: rootpassword: passwordds2:jdbc-url: jdbc:mysql://127.0.0.1:3308/spring-boot-demos?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=trueusername: rootpassword: password

同时,配置了响应的数据源配置:

@Configuration
public class DataSourceConfigurations {@Configuration@MapperScan(basePackages = "com.example.mapper.ds1",sqlSessionFactoryRef = "ds1SqlSessionFactory")public static class Ds1Configuration {public static final String DS1_MAPPER_LOCATION = "classpath*:mapper/ds1/*.xml";@Primary@Bean("ds1DataSource")@ConfigurationProperties(prefix = "spring.datasource.ds1")public DataSource ds1DataSource() {return DataSourceBuilder.create().build();}@Primary@Bean("ds1TransactionManager")public PlatformTransactionManager ds1TransactionManager(@Qualifier("ds1DataSource") DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}@Primary@Bean(name = "ds1SqlSessionFactory")public SqlSessionFactory ds1SqlSessionFactory(@Qualifier("ds1DataSource") DataSource dataSource) throws Exception {MybatisSqlSessionFactoryBean sessionFactoryBean = new MybatisSqlSessionFactoryBean();sessionFactoryBean.setDataSource(dataSource);sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(DS1_MAPPER_LOCATION));return sessionFactoryBean.getObject();}}@Configuration@MapperScan(basePackages = "com.example.ds2",sqlSessionFactoryRef = "ds2SqlSessionFactory")public static class Ds2Configuration {public static final String DS2_MAPPER_LOCATION = "classpath*:mapper/ds2/*.xml";@Bean@ConfigurationProperties(prefix = "spring.datasource.ds2")public DataSource ds2DataSource() {return DataSourceBuilder.create().build();}@Beanpublic PlatformTransactionManager ds2TransactionManager(@Qualifier("ds2DataSource") DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}@Bean(name = "ds2SqlSessionFactory")public SqlSessionFactory ds2SqlSessionFactory(@Qualifier("ds2DataSource") DataSource dataSource) throws Exception {MybatisSqlSessionFactoryBean sessionFactoryBean = new MybatisSqlSessionFactoryBean();sessionFactoryBean.setDataSource(dataSource);sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(DS2_MAPPER_LOCATION));return sessionFactoryBean.getObject();}}
}

相对应的,有两个Mapper:

@Data
@TableName("tb_test1")
public class Test1DO {@TableIdprivate Integer id;private LocalDateTime createTime;private boolean deleted;private String name;private Short status;
}@Mapper
public interface Ds1Test1Mapper extends BaseMapper<Test1DO> {}@Mapper
public interface Ds2Test1Mapper extends BaseMapper<Test1DO> {}

首先,先向ds2数据库的tb_test1表中插入id=1的数据。然后做如下操作:

@RequiredArgsConstructor
@Service
public class Test1ServiceImpl implements ITest1Service {private final Ds1Test1Mapper ds1Test1Mapper;private final Ds2Test1Mapper ds2Test1Mapper;@Override@Transactional(rollbackFor = RuntimeException.class)public void save() {Test1DO test1DO = new Test1DO();test1DO.setId(1);test1DO.setName("test ds2");test1DO.setStatus((short) 1);ds1Test1Mapper.insert(test1DO);ds2Test1Mapper.insert(test1DO);}@Overridepublic void save1() {Test1DO test1DO = new Test1DO();test1DO.setId(1);test1DO.setName("test ds2");test1DO.setStatus((short) 1);ds1Test1Mapper.insert(test1DO);ds2Test1Mapper.insert(test1DO);}
}

当调用save1()时,ds1中插入了一条数据,而ds2中由于id冲突而失败。
如果调用save()时,ds1和ds2中都插入失败(ds1的数要清掉).
这个时候对save()进行断点:

//TransactionAspectSupport.java 377行 (springboot版本2.7.17)
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);

发现ptm是上面配置的Primary TransactionManager。

如果将两个数据库表数据都清空,然后执行save(),对 commit 进行断点:

// DataSourceTransactionManager.doCommit()
protected void doCommit(DefaultTransactionStatus status) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();Connection con = txObject.getConnectionHolder().getConnection();if (status.isDebug()) {logger.debug("Committing JDBC transaction on Connection [" + con + "]");}try {con.commit();}catch (SQLException ex) {throw translateException("JDBC commit", ex);}}

对 con 进行断点发现,其为3307的数据库的连接,然后整个过程没有别的连接了。并且3308的数据也能正常保存。

以上也就是两点:
1、当使用多个SqlSessionFactory配置数据源时,公用同一个TransactionManager,和同一个Connection(3307),这个时候,3308数据正常提交。
2、当提交3308的数据发生异常时,3307的数据也不会插入,即有事务属性。

目前对于上述两点不是很理解,为何只有3307的Connection,3308的数据也能提交,而且为何会有事务性。

可能是作者对SpringBoot的事务不是理解的很深入,希望有大佬能解惑。

这篇关于SpringBoot 使用多SqlSessionFactory下的事务问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/411826

相关文章

如何使用celery进行异步处理和定时任务(django)

《如何使用celery进行异步处理和定时任务(django)》文章介绍了Celery的基本概念、安装方法、如何使用Celery进行异步任务处理以及如何设置定时任务,通过Celery,可以在Web应用中... 目录一、celery的作用二、安装celery三、使用celery 异步执行任务四、使用celery

使用Python绘制蛇年春节祝福艺术图

《使用Python绘制蛇年春节祝福艺术图》:本文主要介绍如何使用Python的Matplotlib库绘制一幅富有创意的“蛇年有福”艺术图,这幅图结合了数字,蛇形,花朵等装饰,需要的可以参考下... 目录1. 绘图的基本概念2. 准备工作3. 实现代码解析3.1 设置绘图画布3.2 绘制数字“2025”3.3

在Ubuntu上部署SpringBoot应用的操作步骤

《在Ubuntu上部署SpringBoot应用的操作步骤》随着云计算和容器化技术的普及,Linux服务器已成为部署Web应用程序的主流平台之一,Java作为一种跨平台的编程语言,具有广泛的应用场景,本... 目录一、部署准备二、安装 Java 环境1. 安装 JDK2. 验证 Java 安装三、安装 mys

Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单

《Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单》:本文主要介绍Springboot的ThreadPoolTaskScheduler线... 目录ThreadPoolTaskScheduler线程池实现15分钟不操作自动取消订单概要1,创建订单后

详谈redis跟数据库的数据同步问题

《详谈redis跟数据库的数据同步问题》文章讨论了在Redis和数据库数据一致性问题上的解决方案,主要比较了先更新Redis缓存再更新数据库和先更新数据库再更新Redis缓存两种方案,文章指出,删除R... 目录一、Redis 数据库数据一致性的解决方案1.1、更新Redis缓存、删除Redis缓存的区别二

oracle数据库索引失效的问题及解决

《oracle数据库索引失效的问题及解决》本文总结了在Oracle数据库中索引失效的一些常见场景,包括使用isnull、isnotnull、!=、、、函数处理、like前置%查询以及范围索引和等值索引... 目录oracle数据库索引失效问题场景环境索引失效情况及验证结论一结论二结论三结论四结论五总结ora

JAVA中整型数组、字符串数组、整型数和字符串 的创建与转换的方法

《JAVA中整型数组、字符串数组、整型数和字符串的创建与转换的方法》本文介绍了Java中字符串、字符数组和整型数组的创建方法,以及它们之间的转换方法,还详细讲解了字符串中的一些常用方法,如index... 目录一、字符串、字符数组和整型数组的创建1、字符串的创建方法1.1 通过引用字符数组来创建字符串1.2

Jsoncpp的安装与使用方式

《Jsoncpp的安装与使用方式》JsonCpp是一个用于解析和生成JSON数据的C++库,它支持解析JSON文件或字符串到C++对象,以及将C++对象序列化回JSON格式,安装JsonCpp可以通过... 目录安装jsoncppJsoncpp的使用Value类构造函数检测保存的数据类型提取数据对json数

Redis事务与数据持久化方式

《Redis事务与数据持久化方式》该文档主要介绍了Redis事务和持久化机制,事务通过将多个命令打包执行,而持久化则通过快照(RDB)和追加式文件(AOF)两种方式将内存数据保存到磁盘,以防止数据丢失... 目录一、Redis 事务1.1 事务本质1.2 数据库事务与redis事务1.2.1 数据库事务1.

python使用watchdog实现文件资源监控

《python使用watchdog实现文件资源监控》watchdog支持跨平台文件资源监控,可以检测指定文件夹下文件及文件夹变动,下面我们来看看Python如何使用watchdog实现文件资源监控吧... python文件监控库watchdogs简介随着Python在各种应用领域中的广泛使用,其生态环境也