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 {
    @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中的数据库操作,并进行回滚。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/204623.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux系统编程 day07 信号

Linux系统编程 day07 信号 1. 信号的介绍以及信号的机制2. 信号相关函数2.1 signal2.2 kill2.3 abort和raise2.4 alarm2.5 setitimer 3. 信号集4. 信号捕捉函数6. SIGCHLD信号7. SIGUSR1与SIGUSR2 1. 信号的介绍以及信号的机制 信号是信息的载体&#xff0c;在Linux/Unix环境下…

行业追踪,2023-11-30

自动复盘 2023-11-30 凡所有相&#xff0c;皆是虚妄。若见诸相非相&#xff0c;即见如来。 k 线图是最好的老师&#xff0c;每天持续发布板块的rps排名&#xff0c;追踪板块&#xff0c;板块来开仓&#xff0c;板块去清仓&#xff0c;丢弃自以为是的想法&#xff0c;板块去留让…

企业数字化转型应对传统网络挑战的关键策略

数字化变革正在以前所未有的速度和规模改变着我们的生活和工作方式&#xff0c;使得传统网络架构面临着巨大的挑战。其中包括带宽需求增加、多云应用增加、安全威胁增加以及传统网络设备无法满足需求等问题。 数字化时代需要更高速、更可靠、更安全的网络支持&#xff0c;传统网…

C语言必备知识--函数返回局部变量

0.总结 1. 不能以局部变量的方式创建字符串数组的首地址 2.如果函数的返回值非要是一个局部变量的地址&#xff0c;那么该局部变量一定要申明为static类型 3.返回指向字符串常量的指针 4.数组不能作为函数返回值 5.在函数中可以返回局部变量的值&#xff0c;但是不能返回…

如何安装鸿蒙Harmony 4.0低版API9三方库

比如我要用下拉刷新三方库pulltorefresh 安装命令如下 ohpm install ohos/pulltorefresh 安装完后然后运行Demo报错,说没有isAtEnd方法 然后查看pulltorefresh 最新版2.0.4对应Harmony API10,然而我的手机是API9,所以必须找到API9的库&#xff0c;然后查看2.0.1是还是API9 所…

docker集群的详解以及超详细搭建

文章目录 一、问题引入1. 多容器位于同一主机2. 多容器位于不同主机 二、介绍三、特性四、概念1. 节点nodes2. 服务(service)和任务(task)3. 负载均衡 五、docker网络1. overlay网络 六、docker集群搭建1. 环境介绍2. 创建集群3. 集群网络4. 加入工作节点 七、部署可视化界面po…

建设中国版MBA在线教育网站,群硕为Quantic敲开中国大门

2024考研即将拉开序幕&#xff0c;一个令人胆寒的问题出现在问答社区热榜—— 从现实来看&#xff0c;学历贬值已经成为一种全球现象。在卷学历的也不仅是大学生&#xff0c;还有很多职场人士&#xff0c;渴望通过获得MBA学位成为精英人才、商业领袖。 Quantic是交互式MBA线上…

热部署怎么部署

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言操作流程&#xff1a;在这里插入图片描述 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/a832d83c091742eda9d9325931a89df4.png) 这里的跟上面的…

【自动化测试】pytest 用例执行中print日志实时输出

author: jwensh date: 20231130 pycharm 中 pytest 用例执行中 print 日志 standout 实时命令行输出 使用场景 在进行 websocket 接口进行测试的时候&#xff0c;希望有一个 case 是一直执行并接受接口返回的数据 def on_message(ws, message):message json.loads(message)…

Liunx配置Tomcat自启动

Liunx配置Tomcat自启动 Tomcat安装配置Tomcat开机启动 Tomcat安装 下载tomcat软件安装包&#xff0c;上传软件包到Liunx服务器。 解压软件包到opt目录下 tar -xvf apache-tomcat-9.0.76.tar.gz -c /opt配置Tomcat开机启动 &#xff08;1&#xff09;修改Tomcat bin目录下的ca…

有”亿“点强,抖音的服务器带宽是如何应对亿人同时刷屏的?

在当今这个“网络横行”的时代&#xff0c;刷短视频已然成为许多人日常生活的一部分。 当我们在刷短视频的时候&#xff0c;尽管家里的网速并不慢&#xff0c;但短视频播放的卡顿却让人难以忍受&#xff0c;有时候真的让人想扔掉手机。 那么&#xff0c;为什么会出现这种情况…

Elk+Filebeat+Kafka实现日志收集

ElkFilebeatKafka实现日志收集(本机nginx) 部署Zookeeper 1.实验组件 #准备3台服务器做Zookeeper集群 20.0.0.10 20.0.0.20 20.0.0.30 2.安装前准备 #关闭防火墙 systemctl stop firewalld systemctl disable firewalld setenforce 0#安装JDK yum install -y java-1.8.0-o…

【LeetCode】每日一题 2023_11_29 无限集中的最小数字(哈希/堆)

文章目录 刷题前唠嗑题目&#xff1a;无限集中的最小数字题目描述代码与解题思路偷看大佬题解 结语 刷题前唠嗑 LeetCode&#xff1f;启动&#xff01;&#xff01;&#xff01; 今天的题目也比较的简单&#xff0c;因为数据量不大&#xff0c;所以什么做法都能过的去 题目&a…

Ruoyi-Vue或者Ruoyi-Cloud登录进去之后的第一个页面如何修改(即如何去掉首页或者如何修改首页)

其实大家如果看过最近的码云上的issues 就能知道这个问题的答案了。 我这里给出一下链接&#xff1a;https://gitee.com/y_project/RuoYi-Vue/issues/I60JIY 开始 第一步&#xff0c;把router/index.js里面关于首页的路由给注释掉或者删除掉&#xff0c;如图&#xff1a; 第…

plt创建指定色系

1、创建不连续色系 import matplotlib.pyplot as plt from matplotlib.colors import ListedColormap# 定义颜色的RGB值 colors [(0.2, 0.4, 0.6), # 蓝色(0.8, 0.1, 0.3), # 红色(0.5, 0.7, 0.2),(0.3,0.5,0.8)] # 绿色# 创建色系 cmap ListedColormap(colors)# 绘制…

化妆品大卖!年轻女孩26天狂赚7000万!

有个年轻的女孩&#xff0c;我们暂且称之为小美。小美经营着一个美丽的小程序商城&#xff0c;里面销售着各种各样的化妆品、日用百货和小家电。这些产品并非什么稀有品牌&#xff0c;但价格比其他地方要优惠一些&#xff0c;更重要的是&#xff0c;购买产品还能赚到钱。 让我们…

微信小程序 - 开发版、体验版、正式版共享本地缓存

问题描述 最近突然发现一个大问题啊&#xff0c;小程序切换版本环境的时候发现数据被污染了&#xff0c;瞬间就怀疑不同环境版本的小程序本地缓存是否共享的&#xff1f;&#xff01; 果然是&#xff01; 解决方案 我们可能马上想到解决方案就是&#xff1a;给每一个环境版本…

人工智能“排头兵”,探访福州多地 AI 智算实践

生成式 AI 在 2023 年再次引爆 IT 技术发展&#xff0c;福建作为数字中国的重要策源地&#xff0c;也是国家数字经济创新发展试验区&#xff0c;在人工智能方面拥有良好的产业基础和人才优势&#xff0c;同时近期出台的《福建省促进人工智能产业发展十条措施》&#xff0c;为福…

【UE】简单的警觉系统

效果 步骤 1. 新建一个空白工程&#xff0c;添加第三人称游戏内容包 2. 打开第三人称角色蓝图“BP_ThirdPersonCharacter” 选中弹簧臂组件&#xff0c;将目标臂长度设置为600&#xff0c;z轴方向的插槽偏移设置为100 3. 将“BP_ThirdPersonCharacter”移入场景&#xff0c;该…

[CustomMessages] section

[CustomMessages] section用来定义自定义的一些{cm:}常量. 一个定义和使用的例子。 [CustomMessages] CreateDesktopIconCreate a &desktop icon[Tasks] Name: desktopicon; Description: "{cm:CreateDesktopIcon}"CustomMessages是支持带参数的&#xff0c;从…