11-@Transaction与AOP冲突解决

news2024/9/17 7:43:38

如题,最近碰到了一个问题,在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 {
    @Autowired
    private UserMapper userMapper;

    @Override
    @Transactional
    public 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 {
    @Autowired
    private IUserService userService;

    @Test
    public 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 {
    @Autowired
    private UserMapper userMapper;

    @Override
    @Transactional
    public 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: null
	at 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: null
	at 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.coloradmin.cn/o/1246056.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Matlab三角剖分插值问题分析

目录 前言 一、问题引入 二、一个例子 1.生成散点图 2.对数据进行剖分 3.点法式分析 三、最后结果 前言 上一篇文章感觉对三角剖分问题没有说清楚&#xff0c;这次专门对三角剖分问题再仔细说说。 一、问题引入 实际上这个问题是用来解决二维曲面插值问题的。 二维插值问题&…

GWAS结果批量整理:升级版算法TidyGWAS

TidyGWAS GWAS分析关键结果之一是显著性SNP位点的P值&#xff0c;通常多年份多地点多模型的GWAS分析将会产生很多结果文件&#xff0c;如何对这些数据进行整理&#xff1f; 汇总这些结果&#xff0c;并将显著性的位点或区域找出来&#xff0c;更加清晰的展示关键信息。 今天介…

文旅虚拟人IP:数字时代的传统文化推荐官

近几年&#xff0c;随着文旅虚拟人频“上岗”&#xff0c;虚拟人逐渐成为了文旅品牌的一种新颖的传统文化传播思路。 文旅品牌定制化推出虚拟人&#xff0c;本质原因是2023旅游业全面复苏&#xff0c;各文旅玩法同质化现象严重&#xff0c;在这样的境遇下&#xff0c;文旅品牌开…

AIGC专题报告:生成式人工智能用例汇编

今天分享的是AIGC系列深度研究报告&#xff1a;《AIGC专题报告&#xff1a;生成式人工智能用例汇编》。 &#xff08;报告出品方&#xff1a;德勤&#xff09; 报告共计&#xff1a;16页 生成式人工智能&#xff08;AI&#xff09;的兴起 生成式AI给人类文明创造了无限的可…

【【Linux 常用命令学习 之 一 】】

Linux 常用命令学习 之 一 打开终端之后的 我们会了解 所使用的 字符串含义 其中前面的 zhuxushuai 是 当前的用户名字 接下来的 zhuxushuai-virtual-machine 是 机器名字 最后的符号 $表示 当前是普通用户 输入指令 ls 是打印出当前所在目录中所有文件和文件夹 shell 操…

python-冒泡排序

冒泡排序 &#xff08;稳定&#xff09; O(n^2) (稳定&#xff1a;表示相等的数&#xff0c;相对位置会不会改变) 冒泡排序&#xff08;Bubble Sort&#xff09;是一种简单的排序算法&#xff0c;它通过多次遍历待排序的元素&#xff0c;比较相邻两个元素的大小并交换它们&…

周转箱与工具柜的智能化应用

在当今制造业激烈竞争的市场中&#xff0c;6S管理方法作为提高企业竞争力的有力工具&#xff0c;与精益生产中的周转箱和工具柜相结合&#xff0c;将为企业带来更大的优势。通过实施6S管理方法&#xff0c;企业不仅能够提高生产效率、降低成本&#xff0c;还能够改善产品质量、…

​ 一文带你了解多文件混淆加密

​&#x1f512; 一文带你了解多文件混淆加密 目录 &#x1f512; 一文带你了解 JavaScript 多文件混淆加密 ipaguard加密前 ipaguard加密后 ​ &#x1f512; 一文带你了解 JavaScript 多文件混淆加密 JavaScript 代码多文件混淆加密可以有效保护源代码不被他人轻易盗取。…

牛客 HJ106 字符逆序 golang实现

牛客题目算法连接 题目 golang 实现 package mainimport ("fmt""bufio""os" )func main() {str, _ : bufio.NewReader(os.Stdin).ReadString(\n)if len(str) 0 {return } else {newstr:""strLen:len(str)-1for i:strLen;i>0;i-…

小红书达人类型特点有哪些,创作形式总结!

小红书自带的社交电商属性&#xff0c;吸引了众多优秀的内容创作者和品牌达人。他们以不同的风格和主题&#xff0c;赢得了粉丝们的喜爱和关注。今天为大家分享下小红书达人类型特点有哪些&#xff0c;创作形式总结&#xff01; 1. 内容创作风格 我们从内容上来区分小红书达人类…

Android11编译第七弹:串口文件读写

问题&#xff1a;需要对SIM卡进行管理&#xff0c;支持APP切换SIM卡。此功能需要访问串口文件&#xff0c;并且对串口文件进行读写。APP操作串口文件/dev/ttyUSB1时&#xff0c;串口文件打开失败。 2023-11-23 10:59:44.092 14264-14264 MULTI_CARD_SerialHandle com.wellnkio…

2023.11.24制作一个常用的登录注册模板(包含密码验证、输出格式验证、验证码等功能)

2023.11.24制作一个常用的登录注册模板&#xff08;包含密码验证、输出格式验证、验证码等功能&#xff09; 1. 简介2. 功能3. 页面效果3.1 登录页面3.2 忘记密码页3.3 注册页面 1. 简介 比较喜欢简洁风&#xff0c;只是用bootstrap进行简单装饰 制作一个模板&#xff0c;日常…

2000-2022年上市公司全要素生产率LP方法(含原始数据+测算代码do文档+计算结果)

2000-2022年上市公司全要素生产率测算LP法&#xff08;含原始数据测算代码do文档计算结果&#xff09; 1、时间&#xff1a;2000-2022年 2、范围&#xff1a;上市公司 3、指标&#xff1a;证券代码、证券简称、统计截止日期、固定资产净额、year、股票简称、报表类型编码、折…

国自然基金项目的题目确定、立项依据、特色与创新、研究内容 目标等,基金撰写的隐藏技巧

目录 一 基金项目申请要求、重点及项目介绍 二 基金的撰写技巧 三 基金撰写的隐藏技巧 四 范例分析及提交前的自我审查 更多应用 基金项目申请需要进行跨学科的技术融合&#xff0c;申请人需要与不同领域结合&#xff0c;形成多学科交叉的研究。基金项目申请在新时期更加注…

函数计算的新征程:使用 Laf 构建 AI 知识库

Laf 已成功上架 Sealos 模板市场&#xff0c;可通过 Laf 应用模板来一键部署&#xff01; 这意味着 Laf 在私有化部署上的扩展性得到了极大的提升。 Sealos 作为一个功能强大的云操作系统&#xff0c;能够秒级创建多种高可用数据库&#xff0c;如 MySQL、PostgreSQL、MongoDB …

今日祝福语道一声早安,惟愿你时时安好,天天幸福!

1、托太阳公公轻轻的叩醒你沉睡的心灵&#xff0c;将暖暖的阳光洒满你全身&#xff0c;愿你精神抖擞信心百倍的迎接新的一天&#xff0c;朋友&#xff0c;早安&#xff01; 2、一天中&#xff0c;第一个叫醒你的是闹钟&#xff0c;第一眼看到的是阳光&#xff0c;第一件要做的事…

竹云参编《公共数据授权运营平台技术要求》团体标准正式发布

2023年11月23日&#xff0c;第二届全球数字贸易博览会“数据要素治理与市场化论坛”于杭州成功召开&#xff0c;国家数据局党组书记、局长刘烈宏&#xff0c;浙江省委常委、常务副省长徐文光出席会议并致辞。会上&#xff0c;国家工业信息安全发展研究中心发布并解读了我国首部…

走近科学之《MySQL 的秘密》

走近科学之《MySQL 的秘密》 mysql 存储引擎、索引、执行计划、事务、锁、分库分表、优化 1、存储引擎&#xff08;storage engines&#xff09; 存储引擎规定了数据存储时的不同底层实现&#xff0c;如存储机制、索引、锁、事务等。 可以通过 show engines 命令查看当前服务…

RabbitMQ基础教程

1.什么是消息队列 消息队列&#xff08;Message Queue&#xff09;&#xff0c;我们一般简称为MQ。消息队列中间件是分布式系统中重要的组件&#xff0c;具有异步性、松耦合、分布式、可靠性等特点。用于实现高性能、高可用、可伸缩和最终一致性架构。是大型分布式系统不可缺少…

利用企业被执行人信息查询API保障商业交易安全

前言 在当今竞争激烈的商业环境中&#xff0c;企业为了保障商业交易的安全性不断寻求新的手段。随着技术的发展&#xff0c;利用企业被执行人信息查询API已经成为了一种强有力的工具&#xff0c;能够帮助企业在商业交易中降低风险&#xff0c;提高合作的信任度。 企业被执行人…