11-@Transaction與AOP沖突解決

如題,最近碰到了一個問題,在public方法上添加@Transaction沒有生效,事務沒有回滾。
我自己模擬了一個功能,向數據庫表User里面插入用戶數據。說一下代碼背景,
數據庫MySQL,持久化層Mybatis,項目使用SpringBoot。
數據表User如下,已有一條數據:
table表:User
下面是代碼

  1. Application啟動類
package com.example.demo;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import tk.mybatis.spring.annotation.MapperScan;@SpringBootApplication(scanBasePackages = {"com.example.demo","com.example.demo.dao"})
//啟動事務
@EnableTransactionManagement
//mapper接口掃描
@MapperScan("com.example.demo.dao")
@RestController
public class DemoApplication {@GetMapping("/")String home() {return "Spring is here!";}public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}
}
  1. 實體類User,對應數據表user表
@Data
@Table(name = "`user`")
public class User {@Id@Column(name = "`id`")@GeneratedValue(strategy = GenerationType.IDENTITY)private int id;@Column(name = "`name`")private String name;@Column(name = "`created_time`")private Date createdTime;
}
  1. Mapper類
package com.example.demo.dao;import com.example.demo.domain.po.User;
import org.springframework.stereotype.Repository;
import tk.mybatis.mapper.common.Mapper;
import tk.mybatis.mapper.common.MySqlMapper;/*** TOrderMapper** @author zhouxy@133.cn* @date 2022/8/15**/
@Repository
public interface UserMapper extends Mapper<User>, MySqlMapper<User> {
}
  1. Service接口,處理業務邏輯,添加@Transaction
package com.example.demo.service;import com.example.demo.domain.po.User;
import org.springframework.stereotype.Service;/*** @Description: TODO* @Author: zhouxy* @CreateTime: 2023-10-17*/
public interface IUserService {Integer addUser(User user);
}
  1. service實現類
package com.example.demo.service.impl;import com.example.demo.dao.UserMapper;
import com.example.demo.domain.po.User;
import com.example.demo.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;/*** @Description: TODO* @Author: zhouxy* @CreateTime: 2023-10-17*/
@Service
public class UserService implements IUserService {@Autowiredprivate UserMapper userMapper;@Override@Transactionalpublic Integer addUser(User user) {Integer result = userMapper.insertSelective(user);}
}
  1. AOP類
package com.example.demo.aop;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;/*** @Description: TODO* @Author: zhouxy* @CreateTime: 2023-05-17*/
@Aspect
@Component
@Slf4j
public class LogAop {@Pointcut(value = "execution(* com.example.demo.service.*.*(..))")public void controller() {log.info("223432");}@Around("controller()")public Object around(ProceedingJoinPoint joinPoint) {log.info("before================");try {Object res = joinPoint.proceed();return res;} catch (Throwable throwable) {log.error("", throwable);}finally {log.info("after================");}return null;}
}
  1. 測試類
import com.example.demo.DemoApplication;
import com.example.demo.dao.UserMapper;
import com.example.demo.domain.po.User;
import com.example.demo.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;/*** MybatisTest** @author zhouxy@133.cn* @date 2021/7/20**/
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoApplication.class)
public class MybatisTest {@Autowiredprivate IUserService userService;@Testpublic void test() {User user = new User();user.setName("LISI222");userService.addUser(user);}
}

當正常執行的時候,會在數據庫中插入數據。
table表:User

打印日志:
在這里插入圖片描述

此時在實現類中加入一行異常,看一下出現異常之后,事務是否會回滾,數據能否成功插入到數據表中。

package com.example.demo.service.impl;import com.example.demo.dao.UserMapper;
import com.example.demo.domain.po.User;
import com.example.demo.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;/*** @Description: TODO* @Author: zhouxy* @CreateTime: 2023-10-17*/
@Service
public class UserService implements IUserService {@Autowiredprivate UserMapper userMapper;@Override@Transactionalpublic Integer addUser(User user) {Integer result = userMapper.insertSelective(user);//添加一行異常,查看事務是否會因為拋出異常回滾剛才的插入操作throw new RuntimeException();}
}

我們看執行完之后,數據庫的變化,我剛才插入的是用戶:LISI222
在這里插入圖片描述

顯示的是插入成功,看后臺日志顯示,事務并沒有做回滾操作,說明當前事務并沒有捕獲到異常信息。仔細分析日志頭尾可知,日志打印操作包裹了service實現類的addUser方法,應該是aop操作在@Transaction之前執行,導致異常被aop處理了,aop最終也沒有拋出異常,所以@Transaction方法沒有捕獲到異常信息。

2023-11-24 10:53:24.418  INFO [,,,] 29600 --- [           main] com.example.demo.aop.LogAop              : before================
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6c3884f5]
JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5542418c] will be managed by Spring
==>  Preparing: INSERT INTO `user` ( `id`,`name` ) VALUES( ?,? )
==> Parameters: 0(Integer), LISI222(String)
<==    Updates: 1
==>  Executing: SELECT LAST_INSERT_ID()
<==    Columns: LAST_INSERT_ID()
<==        Row: 11
<==      Total: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6c3884f5]
2023-11-24 10:53:24.566 ERROR [,,,] 29600 --- [           main] com.example.demo.aop.LogAop              : java.lang.RuntimeException: nullat com.example.demo.service.impl.UserService.addUser(UserService.java:24) ~[classes/:na]at com.example.demo.service.impl.UserService$$FastClassBySpringCGLIB$$9c38b50f.invoke(<generated>) ~[classes/:na]····異常信息打印,忽略2023-11-24 10:53:24.566  INFO [,,,] 29600 --- [           main] com.example.demo.aop.LogAop              : after================
這里仍然正常提交事務,說明沒有捕獲到異常信息
Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6c3884f5]
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6c3884f5]
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6c3884f5]

現在是因為aop中沒有將異常拋出,做了友好化處理,將異常處理成了返回了錯誤碼。所以需要在aop處理返回結果之前,事務就要捕獲到當前事務中拋出的異常并進行回滾或者不提交事務。
我在網上查找資料的時候查找到@Order注解,@Order的作用是定義Spring IOC容器中Bean的執行順序的優先級(這里的順序也可以理解為存放到容器中的先后順序),所以我給LogAop類加了一個@Order注解,讓LogAOP在最后執行,這樣@Transaction類就可以提前執行,捕獲異常,回滾數據。@Order默認為最低優先級,因此可以直接設置@Order即可

@Aspect
@Component
@Slf4j
@Order(10) //定義LogAOP的順序最后執行,值越高,優先級越低
public class LogAop {
·····
}

這次再執行一遍,查看是否成功回滾事務。
在這里插入圖片描述
再看下日志,事務在aop之前執行完成。

2023-11-24 10:59:04.879  INFO [,,,] 28412 --- [           main] com.example.demo.aop.LogAop              : before================
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@786e2c5e]
JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@78854721] will be managed by Spring
==>  Preparing: INSERT INTO `user` ( `id`,`name` ) VALUES( ?,? )
==> Parameters: 0(Integer), LISI333(String)
<==    Updates: 1
==>  Executing: SELECT LAST_INSERT_ID()
<==    Columns: LAST_INSERT_ID()
<==        Row: 12
<==      Total: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@786e2c5e]
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@786e2c5e]
這里關閉了事務,并且沒有提交操作。
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@786e2c5e]
2023-11-24 10:59:05.271 ERROR [,,,] 28412 --- [           main] com.example.demo.aop.LogAop              : java.lang.RuntimeException: nullat com.example.demo.service.impl.UserService.addUser(UserService.java:24) ~[classes/:na]at com.example.demo.service.impl.UserService$$FastClassBySpringCGLIB$$9c38b50f.invoke(<generated>) ~[classes/:na]
2023-11-24 10:59:05.272  INFO [,,,] 28412 --- [           main] com.example.demo.aop.LogAop              : after================

使用@Order注解可以調整運行級別,但是需注意一個問題,aop中不要做一些業務操作,因為transaction不會捕獲到aop中拋出的異常。可以使用try…catch捕獲aop中的數據庫操作,并進行回滾。

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

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

相關文章

Vue3(setup)中使用vue-cropper圖片上傳裁剪插件,復制代碼直接使用

最近在項目中用到上傳裁剪&#xff0c;看了一下代碼&#xff0c;覺得這插件可可以。梳理了一下代碼分享給大家 前端UI組件element-plus 如果你也用到了 &#xff0c;快速幫你解決了問題,別忘記點贊收藏 1.首先看效果圖 因為版本vue-cropper 眾多 &#xff0c;雖然網上有各…

阿里云windwos 安裝oracle數據庫,外部用工具連接不上,只能在服務器本機通過127.0.0.1 連接

1. 首先檢查阿里云服務器安全組端口是否開放 oracle 數據庫端口 2. 其次找到oracle 安裝的目錄&#xff0c;打開這倆個文件&#xff0c;將localhost 修改為 服務器本機名稱 3.重啟oracle 監聽服務&#xff0c;就可以連接了

ModuleNotFoundError: No module named ‘Tkinter‘

ModuleNotFoundError: No module named ‘Tkinter’ Windows 不要用 import tkinter 用from tkinter import * from tkinter import * root Tk() w Label(root, text"Hello, world!") w.pack() root.mainloop()mac python 3.10版本 brew install python-tk3.1…

技術部工作職能規劃分析

前言 技術部的職能。以下是一個基本的框架,其中涵蓋了技術部在公司中的關鍵職能和子職能。 主要職能 技術部門的主要職能分為以下幾個板塊: - 技術規劃與戰略: 制定技術規劃和戰略,與業務團隊合作確定技術需求。 研究和預測技術趨勢,引領公司在技術創新和數字化轉型方…

基于springboot實現智慧黨建系統項目【項目源碼】計算機畢業設計

基于springboot實現智慧黨建系統演示 Java技術 Java是由Sun公司推出的一門跨平臺的面向對象的程序設計語言。因為Java 技術具有卓越的通用性、高效性、健壯的安全性和平臺移植性的特點&#xff0c;而且Java是開源的&#xff0c;擁有全世界最大的開發者專業社群&#xff0c;所以…

【Unity細節】Unity中為什么用字符串加載對象,檢查多便都加載不出來—(命名細節)

&#x1f468;?&#x1f4bb;個人主頁&#xff1a;元宇宙-秩沅 hallo 歡迎 點贊&#x1f44d; 收藏? 留言&#x1f4dd; 加關注?! 本文由 秩沅 原創 &#x1f636;?&#x1f32b;?收錄于專欄&#xff1a;unity細節和bug &#x1f636;?&#x1f32b;?優質專欄 ?【…

【Python】itertools模塊,補充:可迭代對象、迭代器

Python中 itertools模塊創建高效迭代器、處理序列數據集。 此模塊所有函數返回迭代器&#xff0c;可用for循環獲取迭代器中的內容&#xff0c;也可用list(...)用列表形式顯示內容。 import itertools[ x for x in dir(itertools) if not x.startswith(_)] # 結果&#xff1a;…

什么是網絡爬蟲技術?它的重要用途有哪些?

網絡爬蟲&#xff08;Web Crawler&#xff09;是一種自動化的網頁瀏覽程序&#xff0c;能夠根據一定的規則和算法&#xff0c;從互聯網上抓取和收集數據。網絡爬蟲技術是隨著互聯網的發展而逐漸成熟的一種技術&#xff0c;它在搜索引擎、數據挖掘、信息處理等領域發揮著越來越重…

Centos/Linux安裝Apahce出現bug匯總

源碼安裝Apache軟件 使用軟件&#xff1a;Apahce2.4.58&#xff0c;apr1.5.2&#xff0c; apr-util1.5.4 1.下載apr、apr-util和Apache軟件&#xff1b; 2.安裝apr壓縮包&#xff0c;步驟如下&#xff1a; 第一、解壓縮 tar zxvf apr-1.5.2.tar.gz第二、安裝 cd /usr/local/sr…

RAID的應用場景以及優缺點

RAID 0(條帶化)&#xff1a; 工作原理&#xff1a; 數據被分成塊&#xff0c;每個塊寫入不同的驅動器&#xff0c;以并行方式提高讀寫性能。 優勢&#xff1a; 卓越的性能提升&#xff0c;特別是對于大型文件的讀寫操作。 劣勢&#xff1a; 完全沒有冗余&#xff0c;一個驅動器…

MFC 中創建并顯示二維碼

1.創建并顯示 QRcode* pQR_Encode; pQR_Encode QRcode_encodeString("12345678901234567890", 0, QR_ECLEVEL_H, QR_MODE_8, 1); if (pQR_Encode) { int nBmpWidth pQR_Encode->width; //獲取控件的邊界大小 CRect rect; Ge…

通俗理解詞向量模型,預訓練模型,Transfomer,Bert和GPT的發展脈絡和如何實踐

最近研究GPT&#xff0c;深入的從transfomer的原理和代碼看來一下&#xff0c;現在把學習的資料和自己的理解整理一下。 這個文章寫的很通俗易懂&#xff0c;把transformer的來龍去脈&#xff0c;還舉例了很多不錯的例子。 Transformer通俗筆記&#xff1a;從Word2Vec、Seq2S…

6 個有效且可用的頂級 Android 數據恢復工具

經過測試 42 種數據恢復軟件產品&#xff0c;發現奇客數據恢復安卓版是 Android 設備的最佳選擇。 過去幾十年來&#xff0c;我一直在科技行業工作&#xff0c;經常幫助人們應對計算機災難&#xff0c;包括丟失數據。 Android 數據恢復應用程序不在您的設備上運行&#xff0c…

IDEA中注釋快捷鍵及模板

單行注釋 將光標放置于要注釋所在行&#xff0c;使用 Ctrl /&#xff0c; 添加行注釋&#xff0c;再次使用&#xff0c;去掉行注釋 若需要將多行進行單行注釋&#xff0c;只需要選中要注釋的多行&#xff0c;然后使用 Ctrl /&#xff0c; 添加行注釋&#xff0c;再次使用&a…

【PTA題目】L1-6 整除光棍 分數 20

L1-6 整除光棍 分數 20 全屏瀏覽題目 切換布局 作者 翁愷 單位 浙江大學 這里所謂的“光棍”&#xff0c;并不是指單身汪啦~ 說的是全部由1組成的數字&#xff0c;比如1、11、111、1111等。傳說任何一個光棍都能被一個不以5結尾的奇數整除。比如&#xff0c;111111就可以被…

leetcode中“復雜的二分”類題目

復雜的二分題目難點 第 410、1011、1482、1552、1760、2187、2226 題 1 根據題意確定二分的數據范圍 2 避免死循環: 決定是int m (leftright1)/2還是int m (leftright)/2 3 返回結果的指針是left還是right 1 LC875. 愛吃香蕉的珂珂 class Solution {public int minEati…

聚焦數據要素跨域運營,構建數據要素統一大市場地方數據局局長閉門會正式召開

11月23日&#xff0c;在第二屆全球數字貿易博覽會期間&#xff0c;杭州市數據資源局、中國電子云、杭州數據交易所聯合組織各地數據主管部門&#xff0c;召開構建數據要素統一大市場地方數據局局長閉門會&#xff0c;交流數據要素統一大市場構建思路&#xff0c;共探公共數據運…

寫給女朋友的python軟件開發教程——從入門到實踐01——總體規劃

文章目錄 學習路徑chatGPT文心一言 學習資源推薦理論學習——一些這些分別錄制視頻講解&#xff08;后面會更&#xff09;實戰——以自己想開發的一個軟件為例進行教學 學習路徑 問&#xff1a; 我已經有python基礎了&#xff0c;想快速學會用python的pyqt開發單機軟件&#x…

人人都會Blazor—— 3.2 組件

Blazor 應用是使用 Razor 組件(非正式地稱為 Blazor 組件或組件)構建的。 組件是用戶界面 (UI) 的自包含部分,具有用于啟用動態行為的處理邏輯。 組件可以嵌套、重用、在項目間共享,并可在 MVC 和 Razor Pages 應用中使用。 組件呈現為瀏覽器文檔對象模型 (DOM) 的內存中表…

通過一個例子理解pytest的fixture的使用

需求 希望編寫登陸web后做一些操作的測試用例&#xff0c;使用pytest框架具體測試用例執行前&#xff0c;需要先拿到web的token&#xff0c;這個獲取token的動作只執行一次 例一 先上測試用例代碼 adminpc-1:~$ cat my_test.py import pytestclass TestWebLogin:pytest.fi…