解決SpringBoot使用@Transactional進行RestTemplate遠程調用導致查詢數據記錄為null的bug

開啟事務過程中,如果遠程調用查詢當前已經開啟但沒有提交的事務,就會查不到數據。

示例代碼

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;import java.util.Objects;@Slf4j
@RestController
@RequestMapping("simple")
@RequiredArgsConstructor
public class SimpleController {private final SimpleObjectMapper simpleObjectMapper;private final RestTemplate restTemplate;/*** 開啟事務過程中,如果遠程調用查詢當前已經開啟但沒有提交的事務,就會查不到數據。*/@GetMapping("insert")@Transactional(rollbackFor = Exception.class)//1.開啟事務public void insert() {SimpleObject simpleObject = new SimpleObject();simpleObject.setId(2);simpleObject.setName("name" + System.currentTimeMillis());simpleObjectMapper.insert(simpleObject);//2.因為開啟了事務,所以這里的 insert 并沒有 commit,導致下面遠程調用查詢數據是空的。SimpleObject simpleObject1 = restTemplate.getForEntity("http://localhost:8080/simple/2", SimpleObject.class, simpleObject.getId()).getBody();log.info("simpleObject1 (這里會輸出null):{}",simpleObject1);//3.這里會輸出null,因為事務沒有提交,數據庫不會新增數據if (Objects.isNull(simpleObject1)) {throw new RuntimeException("simpleObject1 is null");}}/*** 被遠程調用的查詢方法*/@GetMapping("{id}")public SimpleObject selectById(@PathVariable Integer id) {return simpleObjectMapper.selectById(id);}}

拋出異常

2024-05-23 22:49:44.033  INFO 8668 --- [nio-8080-exec-1] MyBatisSqlParsingPlugin     : Execute SQL:
INSERT INTO simple_object  ( id,name )  VALUES  ( 2,'name1716475783993' )
2024-05-23 22:49:44.119  INFO 8668 --- [nio-8080-exec-2] MyBatisSqlParsingPlugin     : Execute SQL:
SELECT id,name FROM simple_object WHERE id=2 
2024-05-23 22:49:44.149  INFO 8668 --- [nio-8080-exec-1] SimpleController        : simpleObject1 (這里會輸出null):null
2024-05-23 22:49:44.161 ERROR 8668 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : 
java.lang.RuntimeException: simpleObject1 is null

解決辦法

1、去掉@Transactional注解(顯然不可能,除非不影響正常的業務
2、手動控制事務,但是有些情況下可能不適用,不適用的情況可以使用分布式事務,如:seata等(推薦

手動控制事務

示例代碼

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;import java.util.Objects;@Slf4j
@RestController
@RequestMapping("simple")
@RequiredArgsConstructor
public class SimpleController {private final SimpleObjectMapper simpleObjectMapper;private final RestTemplate restTemplate;private final PlatformTransactionManager platformTransactionManager;//事務管理器private final TransactionDefinition transactionDefinition;// 事務的一些基礎信息,如超時時間、隔離級別、傳播屬性等/*** 使用手動事務,遠程調用可以查詢到數據。*/@GetMapping("insert2")public void insert2() {//手動事務不能加@Transactional注解,否則優先使用@Transactional注解的事務TransactionStatus transaction = platformTransactionManager.getTransaction(transactionDefinition);//1、手動獲取事務SimpleObject simpleObject = new SimpleObject();simpleObject.setId(2);simpleObject.setName("name" + System.currentTimeMillis());simpleObjectMapper.insert(simpleObject);platformTransactionManager.commit(transaction);//2.手動提交事務SimpleObject simpleObject1 = restTemplate.getForEntity("http://localhost:8080/simple/2", SimpleObject.class, simpleObject.getId()).getBody();log.info("simpleObject1 (這里會輸出simpleObject1):{}",simpleObject1);//3.這里會輸出id = 2的 SimpleObject 對象,因為事務提交,數據庫會新增數據if (Objects.isNull(simpleObject1)) {throw new RuntimeException("simpleObject1 is null");}}/*** 被遠程調用的查詢方法*/@GetMapping("{id}")public SimpleObject selectById(@PathVariable Integer id) {return simpleObjectMapper.selectById(id);}}

不拋出異常,可以正常獲取數據

2024-05-23 23:02:00.506  INFO 10608 --- [nio-8080-exec-1] c.f.m.plugin.MyBatisSqlParsingPlugin     : Execute SQL:
INSERT INTO simple_object  ( id,
name )  VALUES  ( 2,
'name1716476520466' )
2024-05-23 23:02:00.592  INFO 10608 --- [nio-8080-exec-2] c.f.m.plugin.MyBatisSqlParsingPlugin     : Execute SQL:
SELECT id,name FROM simple_object WHERE id=2 
2024-05-23 23:02:00.641  INFO 10608 --- [nio-8080-exec-1] c.f.m.controller.SimpleController        : simpleObject1 (這里會輸出simpleObject1):SimpleObject(id=2, name=name1716476520466)

不使用事務(不推薦)

示例代碼

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;import java.util.Objects;@Slf4j
@RestController
@RequestMapping("simple")
@RequiredArgsConstructor
public class SimpleController {private final SimpleObjectMapper simpleObjectMapper;private final RestTemplate restTemplate;/*** 不使用事務,遠程調用可以查詢到數據。(不推薦)*/@GetMapping("insert3")public void insert3() {SimpleObject simpleObject = new SimpleObject();simpleObject.setId(2);simpleObject.setName("name" + System.currentTimeMillis());simpleObjectMapper.insert(simpleObject);//1、因為沒有事務,所以這里會新增數據到數據庫SimpleObject simpleObject1 = restTemplate.getForEntity("http://localhost:8080/simple/2", SimpleObject.class, simpleObject.getId()).getBody();log.info("simpleObject1 (這里會輸出simpleObject1):{}",simpleObject1);//2.這里會輸出id = 2的 SimpleObject 對象,因為事務提交,數據庫會新增數據if (Objects.isNull(simpleObject1)) {throw new RuntimeException("simpleObject1 is null");}}/*** 被遠程調用的查詢方法*/@GetMapping("{id}")public SimpleObject selectById(@PathVariable Integer id) {return simpleObjectMapper.selectById(id);}}

不拋出異常,可以正常獲取數據

2024-05-23 23:04:31.889  INFO 10608 --- [nio-8080-exec-6] c.f.m.plugin.MyBatisSqlParsingPlugin     : Execute SQL:
INSERT INTO simple_object  ( id,
name )  VALUES  ( 2,
'name1716476671888' )
2024-05-23 23:04:31.895  INFO 10608 --- [nio-8080-exec-7] c.f.m.plugin.MyBatisSqlParsingPlugin     : Execute SQL:
SELECT id,name FROM simple_object WHERE id=2 
2024-05-23 23:04:31.897  INFO 10608 --- [nio-8080-exec-6] c.f.m.controller.SimpleController        : simpleObject1 (這里會輸出simpleObject1):SimpleObject(id=2, name=name1716476671888)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/13550.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/13550.shtml
英文地址,請注明出處:http://en.pswp.cn/web/13550.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

二叉樹求解大小操作詳解

目錄 一、求所有結點個數 1.1 遞歸思路 1.2 遞歸分支圖 1.3 遞歸棧幀圖 1.4 C語言實現 二、求葉子結點個數 2.1 遞歸思路 2.2 遞歸分支圖 2.3 遞歸棧幀圖 2.4 C語言實現 三、求第K層的結點個數 3.1 遞歸思路 3.2 遞歸分支圖 3.3 遞歸棧幀圖 3.4 C語言實現 四、求…

【python】使用函數名而不加括號是什么情況?

使用函數名而不加括號通常是為了表示對函數本身的引用,而不是調用函數。這種用法通常出現在下面這幾種情況: 作為回調函數傳遞:將函數名作為參數傳遞給其他函數,以便在需要時調用該函數。例如,在事件處理程序或高階函數…

馮喜運:5.24現貨黃金趨勢解讀,黃金原油行情分析及操作建議

【黃金消息面分析】:美國勞工部公布的最新數據顯示,截至5月18日的一周內,首次申請失業救濟人數下降至21.5萬人,創下自去年9月以來的最大降幅。數據公布后,現貨黃金短線下挫6美元,報2362.71美元/盎司。這表明…

2024受歡迎的便簽app是哪個

在繁忙的工作和生活中,便簽app成為了我們不可或缺的小助手。2024年,隨著人們對高效工作和生活品質的追求,選擇一款功能強大且用戶友好的便簽app顯得尤為重要。在眾多選擇中,敬業簽以其出色的記錄與提醒功能,脫穎而出&a…

前端發版如何告知用戶

在具體項目場景中,前端發版后,用戶不手動刷新,則感知不到更新;經常會出現:前端更新了某個功能,導致舊功能使用出現問題,而被用戶提單; 關于這個問題有多種解決方式: We…

Python知識詳解【1】~{正則表達式}

正則表達式是一種用于匹配字符串模式的文本工具,它由一系列普通字符和特殊字符組成,可以非常靈活地描述和處理字符串。以下是正則表達式的一些基本組成部分及其功能: 普通字符:大多數字母和數字在正則表達式中表示它們自己。例如…

指針,指針變量,引用,取地址符,malloce()函數使用,C中“—>” 和“ . ” 作用與區別

目錄 一:指針,指針變量,引用,取地址符: 前提 : 1.“ * ” 的兩種用途 2." & “的兩種用途 2.1:引用 2.2:取地址 補充: 二 : malloc(),動態申請地址空間 1.原型定義…

Dubbo生態之初識dubbo協議

1.RPC框架 在java的發展中,隨著業務的越來越龐大,單體架構的工作繁瑣且耦合度高,因此單體架構過渡到了分布式架構,而分布式架構就必然涉及到各個服務之間的遠程通信(RPC框架),RPC框架如圖所示: 工作流程: a.客戶端調…

查看當前Shell系統環境變量

查看當前Shell系統環境變量 查看命令 env效果 查看Shell變量(系統環境變量自定義變量函數) 命令 set效果 常用系統環境變量 變量名稱含義PATH與windows環境變量PATH功能一樣,設置命令的搜索路徑,以冒號為分割HOME當前用戶主目錄:/rootSH…

有道:一季度業績超市場預期,生成式AI商業化落地進程加快

5月23日,教育科技公司網易有道(NYSE:DAO)公布了2024年第一季度未經審計的財務報告。報告期內,受益于“AI”加“教育”雙輪驅動,業績表現超市場預期,業務健康度大幅改善。 財報顯示,…

5.23小結

1.java項目創新 目前想添加一個自動回復的功能和設置驗證方式有(允許任何人添加,禁止添加,設置回答問題添加,普通驗證添加) 目前只完成畫好前端界面,前端發送請求,還有表的修改 因為涉及表字…

leetcode 210.課程表II

思路:拓補排序 其實就是對于第一個題的問題變了一個問法,上一個題本質上是求有沒有環,這道題本質上就是讓你求出來符合沒有環的路徑輸出而已,本質上沒有什么區別。 不同就在于這里需要你額外開一個數組用來存儲你遍歷這個有向圖…

大語言模型量化方法對比:GPTQ、GGUF、AWQ 包括顯存和速度

GPTQ: Post-Training Quantization for GPT Models GPTQ是一種4位量化的訓練后量化(PTQ)方法,主要關注GPU推理和性能。 該方法背后的思想是,嘗試通過最小化該權重的均方誤差將所有權重壓縮到4位。在推理過程中,它將動態地將其權重去量化為f…

nn.Linear

文章目錄 一、nn.Linear 一、nn.Linear nn.Linear 是 PyTorch 中的一個類,用于定義線性變換(全連接層)。它是神經網絡中常用的一種層類型,作為輸入張量與權重矩陣之間的線性變換。 nn.Linear(in_features, out_features, biasTru…

決策樹最優屬性選擇

本文以西瓜數據集為例演示決策樹使用信息增益選擇最優劃分屬性的過程 西瓜數據集下載:傳送門 首先計算根節點的信息熵: 數據集分為好瓜、壞瓜,所以|y|2根結點包含17個訓練樣例,其中好瓜共計8個樣例,所占比例為8/17壞…

2024-5-4-從0到1手寫配置中心Config之基于h2的config-server

添加依賴 新建的web工程中添加h2的依賴 添加h2的配置 設置數據源和密碼設置初始化sql語句打開h2的控制臺 初始化語句創建一個config表,保存服務配置信息。 完成CRUD接口 controller類 mapper接口 測試 在web控制臺可以看到sql已經初始化完成,crud接口…

前端基礎入門三大核心之HTML篇:深入解析PNG8、PNG16、PNG24與PNG32的差異及網頁應用指南

前端基礎入門三大核心之HTML篇:深入解析PNG8、PNG16、PNG24與PNG32的差異及網頁應用指南 基礎概念與作用說明PNG8PNG16PNG24PNG32 代碼示例與使用場景PNG8示例PNG24示例PNG32示例 性能優化與最佳實踐防范漏洞提示結語與討論 在網頁設計與前端開發中,選擇…

PLC工程師按這個等級劃分是否靠譜?

在工業自動化領域,PLC工程師扮演著至關重要的角色,他們負責構建、維護自動化系統,推動工業4.0進程的發展。成為一名優秀的PLC工程師需要經歷不同境界的發展階段,每個階段都對應著不同的技能要求和責任。以下是PLC工程師的六種級別…

Kotlin協程在android中的使用總結

認識協程 引用官方的一段話 協程通過將復雜性放入庫來簡化異步編程。程序的邏輯可以在協程中順序地表達,而底層庫會為我們解決其異步性。該庫可以將用戶代碼的相關部分包裝為回調、訂閱相關事件、在不同線程(甚至不同機器!)上調度…

JDK、JRE、編譯指令和垃圾回收機制詳解

JDK 全稱 Java SE Development Kit (Java 開發工具包) JVM虛擬機:Java運行的地方 核心類庫:Java提前編好的東西 開發工具: javac,java,jdb,jhat javac:Java編譯器,用于將Java源代碼編譯成Java字節碼文件(.class)。 java: java…