当前位置: 首页 > news >正文

Springboot 多数据源事务

起因

在一个service方法上使用的事务,其中有方法是调用的多数据源orderDB

image.png
image.png
image.png
image.png

但是多数据源没有生效,而是使用的primaryDB

image.png

原因

spring 事务实现的方式

@Transactional 注解为例 (也可以看 TransactionTemplate, 这个流程更简单一点)。
入口:ProxyTransactionManagementConfiguration
(从 config 类入手,需要哪些bean一目了然,然后直接顺着看下去就可以了)
主要有以下3个bean

  • TransactionAttri``buteSource:实现是 AnnotationTransactionAttributeSource, 提供从(存在 @Transactional 注解的)方法上读取事务的属性(注解的属性)的功能
  • TransactionInterceptor:事务方法拦截器的bean,在执行事务方法时,转到 (TransactionAspectSupport#invokeWithinTransaction) 方法,即spring事务处理的主要逻辑。
  • BeanFactoryTransactionAttributeSourceAdvisor:一个advisor(包含一个 Pointcut 切点和一个 Advice 通知),advice就是上面的事务拦截器,Pointcut 切点匹配能通过 TransactionAttributeSource 获取到事务信息的方法。

拦截器逻辑大概如下:
image.png

解决方案

每个数据源手动配置SqlSessionFactory

这种方式是通过手动声明创建orm框架对应的bean来实现多数据源的操作,即每个数据源都自己手动创建一套对用的bean。
不支持多个数据源事务,手动配置较繁琐
(如果使用的spring而不是springboot的话,就不会有这种多数据源的疑问,因为本来就要自己声明bean)

动态数据源(本次使用)

只需要把@Transactional(rollbackFor = Exception.class) 换为@DSTransactional即可
并且抛出异常事务也会回滚

image.png

动态数据源实现原理

同样看一下 DynamicDataSourceAutoConfiguration 这个配置相关的类就大概了解了。

  • DynamicRoutingDataSource: 动态数据源,内部使用 Map 保存了多个数据源。获取 connection 时,根据 ThreadLocal 中的 dsKey 获取对应的数据源
    • 另:对于多数据源事务 (TransactionContext.getXID() isNotEmpty),会返回一个 ConnectionProxy 并暂存到 ConnectionFactory 中, 该 ConnectionProxy 不会执行 commit、rollback、close 操作事务相关的方法。
public Connection getConnection() throws SQLException {String xid = TransactionContext.getXID();if (StringUtils.isEmpty(xid)) {// 非多数据源事务直接获取对应 connectionreturn determineDataSource().getConnection();} else {String ds = DynamicDataSourceContextHolder.peek();ds = StringUtils.isEmpty(ds) ? "default" : ds;// 多数据源事务,使用代理的 connection (屏蔽了 commit 等操作)ConnectionProxy connection = ConnectionFactory.getConnection(ds);return connection == null ? getConnectionProxy(ds, determineDataSource().getConnection()) : connection;}
}// 获取 代理的 connection, 并将其存入 ConnectionFactory, 内部维护一个 ThreadLocal<Map>, 同时会 setAutoCommit(false) 开启事务
private Connection getConnectionProxy(String ds, Connection connection) {ConnectionProxy connectionProxy = new ConnectionProxy(connection, ds);ConnectionFactory.putConnection(ds, connectionProxy);return connectionProxy;
}// DynamicRoutingDataSource
// 从 ThreadLocal 获取当前 dsKey 然后获取对应 datasource
public DataSource determineDataSource() {String dsKey = DynamicDataSourceContextHolder.peek();return getDataSource(dsKey);
}
  • DynamicDataSourceAnnotationInterceptor: 处理 @DS 注解的拦截器,获取 @DS 指定的 datasource 并存入 ThreadLocal 中, 供 DynamicRoutingDataSource 使用
  • dynamicTransactionAdvisor: 处理 @DSTransactional 多数据源事务注解的拦截器,在执行目标方法前,标记为多数据源事务 (TransactionContext.bind(xid)), 执行完后, 通知 ConnectionFactory 中的 connectionProxy 进行事务的 commit 或 rollback。
// DynamicLocalTransactionAdvisor
public Object invoke(MethodInvocation methodInvocation) throws Throwable {if (!StringUtils.isEmpty(TransactionContext.getXID())) {return methodInvocation.proceed();}// 事务是否成功boolean state = true;Object o;String xid = UUID.randomUUID().toString();// 标记当前为 多数据源事务TransactionContext.bind(xid);try {o = methodInvocation.proceed();} catch (Exception e) {state = false;throw e;} finally {// 通知 connectionProxy 进行 commit 或 rollbackConnectionFactory.notify(state);TransactionContext.remove();}return o;
}

相关文章:

Springboot 多数据源事务

起因 在一个service方法上使用的事务,其中有方法是调用的多数据源orderDB 但是多数据源没有生效,而是使用的primaryDB 原因 spring 事务实现的方式 以 Transactional 注解为例 (也可以看 TransactionTemplate&#xff0c; 这个流程更简单一点)。 入口&#xff1a;ProxyTransa…...

Python每日学习

我是从c转来学习Python的&#xff0c;总感觉和c相比Python的实操简单&#xff0c;但是由于写c的代码多了&#xff0c;感觉Python的语法好奇怪 就比如说c的开头要有库&#xff08;就是类似于#include <bits/stdc.h>&#xff09;而且它每一项的代码结束之后要有一个表示结…...

数据库 执行sql添加删除字段

添加字段&#xff1a; ALTER TABLE 表明 ADD COLUMN 字段名 类型 DEFAULT NULL COMMENT 注释 AFTER 哪个字段后面; 效果&#xff1a; 删除字段&#xff1a; ALTER TABLE 表明 DROP COLUMN 字段;...

前端开发:HTML与CSS

文章目录 前言1.1、CS架构和BS架构1.2、网页构成 HTML1.web开发1.1、最简单的web应用程序1.2、HTTP协议1.2.1 、简介1.2.2、 http协议特性1.3.3、http请求协议与响应协议 2.HTML概述3.HTML标准结构4.标签的语法5.基本标签6.超链接标签6.1、超链接基本使用6.2、锚点 7.img标签8.…...

ctfshow解题方法

171 172 爆库名->爆表名->爆字段名->爆字段值 -1 union select 1,database() ,3 -- //返回数据库名 -1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema库名 -- //获取数据库里的表名 -1 union select 1,group_concat(…...

探索 Blockly:自定义积木实例

3.实例 3.1.基础块 无输入 , 无输出 3.1.1.json var textOneJson {"type": "sql_test_text_one","message0": " one ","colour": 30,"tooltip": 无输入 , 无输出 };javascriptGenerator.forBlock[sql_test_te…...

MongoDB教程(二十三):关于MongoDB自增机制

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; 文章目录 引言一、MongoD…...

展馆导览系统架构解析,从需求分析到上线运维

在物质生活日益丰富的当下&#xff0c;人们对精神世界的追求愈发强烈&#xff0c;博物馆、展馆、纪念馆等场所成为人们丰富知识、滋养心灵的热门选择。与此同时&#xff0c;人们对展馆的导航体验也提出了更高要求&#xff0c;展馆导览系统作为一种基于室内外地图相结合的位置引…...

Servlet详解(超详细)

Servlet详解 文章目录 Servlet详解一、基本概念二、Servlet的使用1、创建Servlet类2、配置Servleta. 使用web.xml配置b. 使用注解配置 3、部署Web应用4、处理HTTP请求和生成响应5、处理表单数据HTML表单Servlet 6、管理会话 三、servlet生命周期1、加载和实例化2、初始化3、 请…...

Meta AI引入Imagine Me功能,上传图片输入提示词即可实现个性化照片

AITOP100平台获悉&#xff0c;Meta 公司在 AI 领域再次迈出了重要的步伐&#xff0c;其发布的 Llama 3.1 开源 AI 模型以及对 Meta AI 功能的更新扩充引发了广泛关注。 其中&#xff0c;新引入的“Imagine Me”功能尤为引人注目。在这一功能下&#xff0c;美国地区的用户只需上…...

常用自启设置

一、开机自启动 1、编辑 vi /lib/systemd/system/nginx.service 文件&#xff0c;没有创建一个 touch nginx.service 然后将如下内容根据具体情况进行修改后&#xff0c;添加到nginx.service文件中&#xff1a; [Unit] Descriptionnginx Afternetwork.target remote-fs.targ…...

模块与组件、模块化与组件化的理解

在React或其他现代JavaScript框架中&#xff0c;模块与组件、模块化与组件化是核心概念&#xff0c;它们对于提高代码的可维护性、复用性和开发效率具有重要意义。以下是对这些概念的理解&#xff1a; 模块与组件 模块&#xff08;Module&#xff09; 定义&#xff1a;模块是…...

Rust:cargo的常用命令

1.查看版本 $ cargo --version cargo 1.79.0 (ffa9cf99a 2024-06-03) 2.创建新的项目 $ cargo new hello 创建后的目录结构为 $ tree hello/ hello/ ├── Cargo.toml └── src └── main.rs 3.运行项目 $ cd hello $ cargo run Compiling hello v0.1.0 (/home/c…...

LeetCode 3106.满足距离约束且字典序最小的字符串:模拟(贪心)

【LetMeFly】3106.满足距离约束且字典序最小的字符串&#xff1a;模拟&#xff08;贪心&#xff09; 力扣题目链接&#xff1a;https://leetcode.cn/problems/lexicographically-smallest-string-after-operations-with-constraint/ 给你一个字符串 s 和一个整数 k 。 定义函…...

Elasticsearch 与 MySQL 在查询和插入性能上的深度剖析

在当今的数据处理领域&#xff0c;选择合适的数据库对于应用的性能和效率至关重要。Elasticsearch 和 MySQL 作为两款常用的数据库&#xff0c;它们在查询和插入操作上的性能表现各有千秋。本文将对这两款数据库在这两个关键操作上进行详细的对比分析。 一、引言 随着数据量的…...

day4 vue2以及ElementUI

创建vue2项目 可能用到的命令行们 vue create 项目名称 // 创建项目 cd 项目名称 // 只有进入项目下&#xff0c;才能运行 npm run serve // 运行项目 D: //切换盘符 cd .. // 返回到上一级目录 clear // 清空终端 更改 Vue项目的端口配置 基础语法 项目创建完成之后&#…...

把redis用在Java项目

1. Java连接redis Java连接redis的方式是通过jedis&#xff0c;连接redis需要遵循jedis协议。 1.1 引入依赖 <!--引入java连接redis的驱动--><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version&…...

GORM:优雅的Go语言ORM库

文章目录 引言GORM原理基础使用安装GORM定义模型连接数据库CRUD操作 高级使用关联事务回调 优点结论 引言 在Go语言开发中&#xff0c;数据库操作是不可或缺的一部分。虽然直接使用SQL语句可以灵活地与数据库交互&#xff0c;但随着项目规模的扩大&#xff0c;SQL语句的编写、…...

Golang | Leetcode Golang题解之第279题完全平方数

题目&#xff1a; 题解&#xff1a; // 判断是否为完全平方数 func isPerfectSquare(x int) bool {y : int(math.Sqrt(float64(x)))return y*y x }// 判断是否能表示为 4^k*(8m7) func checkAnswer4(x int) bool {for x%4 0 {x / 4}return x%8 7 }func numSquares(n int) i…...

Oracle系统表空间的加解密

实验环境 数据库选择的是orclpdb1&#xff0c;当前系统表空间未加密&#xff1a; SQL> show con_nameCON_NAME ------------------------------ ORCLPDB1SQL> select TABLESPACE_NAME, STATUS, ENCRYPTED from dba_tablespaces;TABLESPACE_NAME STATUS …...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

MongoDB学习和应用(高效的非关系型数据库)

一丶 MongoDB简介 对于社交类软件的功能&#xff0c;我们需要对它的功能特点进行分析&#xff1a; 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具&#xff1a; mysql&#xff1a;关系型数据库&am…...

多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验

一、多模态商品数据接口的技术架构 &#xff08;一&#xff09;多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如&#xff0c;当用户上传一张“蓝色连衣裙”的图片时&#xff0c;接口可自动提取图像中的颜色&#xff08;RGB值&…...

初学 pytest 记录

安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)

上一章用到了V2 的概念&#xff0c;其实 Fiori当中还有 V4&#xff0c;咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务)&#xff0c;代理中间件&#xff08;ui5-middleware-simpleproxy&#xff09;-CSDN博客…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)

前言&#xff1a; 在Java编程中&#xff0c;类的生命周期是指类从被加载到内存中开始&#xff0c;到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期&#xff0c;让读者对此有深刻印象。 目录 ​…...

Vite中定义@软链接

在webpack中可以直接通过符号表示src路径&#xff0c;但是vite中默认不可以。 如何实现&#xff1a; vite中提供了resolve.alias&#xff1a;通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...

手机平板能效生态设计指令EU 2023/1670标准解读

手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读&#xff0c;综合法规核心要求、最新修正及企业合规要点&#xff1a; 一、法规背景与目标 生效与强制时间 发布于2023年8月31日&#xff08;OJ公报&…...

Visual Studio Code 扩展

Visual Studio Code 扩展 change-case 大小写转换EmmyLua for VSCode 调试插件Bookmarks 书签 change-case 大小写转换 https://marketplace.visualstudio.com/items?itemNamewmaurer.change-case 选中单词后&#xff0c;命令 changeCase.commands 可预览转换效果 EmmyLua…...