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

相关文章

vue使用docxtemplater导出word

《vue使用docxtemplater导出word》docxtemplater是一种邮件合并工具,以编程方式使用并处理条件、循环,并且可以扩展以插入任何内容,下面我们来看看如何使用docxtempl... 目录docxtemplatervue使用docxtemplater导出word安装常用语法 封装导出方

Linux换行符的使用方法详解

《Linux换行符的使用方法详解》本文介绍了Linux中常用的换行符LF及其在文件中的表示,展示了如何使用sed命令替换换行符,并列举了与换行符处理相关的Linux命令,通过代码讲解的非常详细,需要的... 目录简介检测文件中的换行符使用 cat -A 查看换行符使用 od -c 检查字符换行符格式转换将

Java编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

SpringBoot实现数据库读写分离的3种方法小结

《SpringBoot实现数据库读写分离的3种方法小结》为了提高系统的读写性能和可用性,读写分离是一种经典的数据库架构模式,在SpringBoot应用中,有多种方式可以实现数据库读写分离,本文将介绍三... 目录一、数据库读写分离概述二、方案一:基于AbstractRoutingDataSource实现动态

使用Jackson进行JSON生成与解析的新手指南

《使用Jackson进行JSON生成与解析的新手指南》这篇文章主要为大家详细介绍了如何使用Jackson进行JSON生成与解析处理,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 核心依赖2. 基础用法2.1 对象转 jsON(序列化)2.2 JSON 转对象(反序列化)3.

Springboot @Autowired和@Resource的区别解析

《Springboot@Autowired和@Resource的区别解析》@Resource是JDK提供的注解,只是Spring在实现上提供了这个注解的功能支持,本文给大家介绍Springboot@... 目录【一】定义【1】@Autowired【2】@Resource【二】区别【1】包含的属性不同【2】@

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co

使用Python实现快速搭建本地HTTP服务器

《使用Python实现快速搭建本地HTTP服务器》:本文主要介绍如何使用Python快速搭建本地HTTP服务器,轻松实现一键HTTP文件共享,同时结合二维码技术,让访问更简单,感兴趣的小伙伴可以了... 目录1. 概述2. 快速搭建 HTTP 文件共享服务2.1 核心思路2.2 代码实现2.3 代码解读3.

Elasticsearch 在 Java 中的使用教程

《Elasticsearch在Java中的使用教程》Elasticsearch是一个分布式搜索和分析引擎,基于ApacheLucene构建,能够实现实时数据的存储、搜索、和分析,它广泛应用于全文... 目录1. Elasticsearch 简介2. 环境准备2.1 安装 Elasticsearch2.2 J