您的位置:首页 > 房产 > 家装 > 同一个类中,一个无事务方法调用一个有事务方法时,事务失效

同一个类中,一个无事务方法调用一个有事务方法时,事务失效

2024/10/7 2:27:55 来源:https://blog.csdn.net/m0_72167535/article/details/140890298  浏览:    关键词:同一个类中,一个无事务方法调用一个有事务方法时,事务失效

目录

1.事务的使用

2.事务的实现原理

3.原因

4.Spring容器管理的代理对象的生成条件和时机

5.事务失效代码


1.事务的使用

        在spring项目的开发中,通过在方法上添加Transactional注解,实现事务的管理,在方法开始开启事务,出现异常进行事务的回滚,方法结束前提交事务。

2.事务的实现原理

   Transactional 注解是 Spring 框架用来实现声明式事务管理的重要工具。其原理主要基于 AOP(面向切面编程),通过动态代理在方法执行前、后以及异常情况下进行事务的处理。

        当你在一个方法上使用 @Transactional 注解时,Spring 会在运行时生成一个代理对象,该对象会拦截对该方法的调用。在调用之前,代理会开始一个新的事务;在方法执行完成后,代理会提交或回滚事务,具体取决于方法是否抛出了未处理的异常。

具体流程如下:

  • spring使用jdk动态代理技术或者cglib代理来创建目标类的代理对象
  • 当方法被调用时,实际上调用的是代理类中的增强方法,而不是直接调用目标类中的方法
  • 在调用方法之前,代理会根据注解的属性(传播行为和隔离级别)从PlatformTransactionManager中获取一个事务,在调用目标方法前开启事务
  • 执行目标方法
  • 目标方法执行成功,则提交事务,执行失败,则回滚事务

代理的两种方式:

  • JDK 动态代理:当目标类实现至少一个接口时,Spring会使用JDK动态代理。这种代理仅适用于基于接口的代理。原理是通过Java反射机制,在运行时生成一个实现了目标类接口的代理类。
  • CGLIB 代理:如果目标类没有实现任何接口,或者您强制配置为使用CGLIB,那么Spring会使用CGLIB库生成一个目标类的子类作为代理。原理是使用CGLIB库,通过继承目标类并重写其方法来实现代理。

3.原因

在同一个类中,一个无事务方法直接调用有事务的方法时,是通过this.方法名的方式调用。

this方式:它直接访问的是当前对象的实现,如果当前类被Spring AOP代理,那么使用this调用的方法将不会触发AOP切面。也就是说,切面(如事务管理、日志记录等)不会生效,因为你直接调用了目标对象的方法,而不是代理对象的方法。

通过Autowired注解注入的bean进行调用的方式:是通过spring容器管理的代理对象进行调用,这种情况下AOP特性可以正常工作,例如事务、日志等会生效。

4.Spring容器管理的代理对象的生成条件和时机

在Spring中,代理对象的生成通常与AOP(面向切面编程)相关,当类上使用@Component@Service@Repository@Controller等注解,并且方法上使用AOP相关的注解时,Spring会创建代理对象,以便能够在调用这些方法时执行切面逻辑。

常见的AOP注解包括:

  • @Transactional
  • @Cacheable
  • @Async
  • @Scheduled

5.事务失效代码

package com.ruoyi.system.service.impl;import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.system.domain.SysConfig;
import com.ruoyi.system.mapper.SysConfigMapper;
import com.ruoyi.system.mapper.SysDeptMapper;
import com.ruoyi.system.service.TestTransactionalService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;/*** @Author linaibo* @Date 2024/8/3 15:36* @Version 1.0*/
@Service
public class TestTransactionalServiceImpl implements TestTransactionalService {@Autowiredprivate SysDeptMapper deptMapper;@Autowiredprivate SysConfigMapper configMapper;@Overridepublic int insertDept(SysDept dept) {dept.setAncestors("123123");deptMapper.insertDept(dept);insertConfig();return 1;}@Override@Transactional(rollbackFor = Exception.class)public int insertConfig() {SysConfig config;for (int i = 0; i < 5; i++) {config = new SysConfig();config.setConfigName("配置" + i);configMapper.insertConfig(config);if (i == 2) {throw new RuntimeException();}}return 1;}
}

3.解决方法

①自己autowire自己(也可以将方法放到另外一个service中,然后注入该service进行调用)

package com.ruoyi.system.service.impl;import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.system.domain.SysConfig;
import com.ruoyi.system.mapper.SysConfigMapper;
import com.ruoyi.system.mapper.SysDeptMapper;
import com.ruoyi.system.service.TestTransactionalService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;/*** @Author linaibo* @Date 2024/8/3 15:36* @Version 1.0*/
@Service
public class TestTransactionalServiceImpl implements TestTransactionalService {@Autowiredprivate SysDeptMapper deptMapper;@Autowiredprivate SysConfigMapper configMapper;@Autowiredprivate TestTransactionalService testTransactionalService;@Overridepublic int insertDept(SysDept dept) {dept.setAncestors("123123");deptMapper.insertDept(dept);testTransactionalService.insertConfig();return 1;}@Override@Transactional(rollbackFor = Exception.class)public int insertConfig() {SysConfig config;for (int i = 0; i < 5; i++) {config = new SysConfig();config.setConfigName("配置" + i);configMapper.insertConfig(config);if (i == 2) {throw new RuntimeException();}}return 1;}
}

②通过spring上下文获取到当前代理类

package com.ruoyi.system.service.impl;import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.system.domain.SysConfig;
import com.ruoyi.system.mapper.SysConfigMapper;
import com.ruoyi.system.mapper.SysDeptMapper;
import com.ruoyi.system.service.TestTransactionalService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;/*** @Author linaibo* @Date 2024/8/3 15:36* @Version 1.0*/
@Service
public class TestTransactionalServiceImpl implements TestTransactionalService {@Autowiredprivate SysDeptMapper deptMapper;@Autowiredprivate SysConfigMapper configMapper;@Autowiredprivate TestTransactionalService testTransactionalService;@Autowiredprivate ApplicationContext applicationContext;@Overridepublic int insertDept(SysDept dept) {dept.setAncestors("123123");deptMapper.insertDept(dept);TestTransactionalService service = applicationContext.getBean(TestTransactionalService.class);service.insertConfig();return 1;}@Override@Transactional(rollbackFor = Exception.class)public int insertConfig() {SysConfig config;for (int i = 0; i < 5; i++) {config = new SysConfig();config.setConfigName("配置" + i);configMapper.insertConfig(config);if (i == 2) {throw new RuntimeException();}}return 1;}
}

③使用AopContext获取到当前代理类,需要在启动类加上EnableAspectJAutoProxy(exposeProxy = true),exposeProxy = true用于控制AOP框架公开代理,公开后才可以通过AopContext获取到当前代理类。

package com.ruoyi.system.service.impl;import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.system.domain.SysConfig;
import com.ruoyi.system.mapper.SysConfigMapper;
import com.ruoyi.system.mapper.SysDeptMapper;
import com.ruoyi.system.service.TestTransactionalService;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.Objects;/*** @Author linaibo* @Date 2024/8/3 15:36* @Version 1.0*/
@Service
public class TestTransactionalServiceImpl implements TestTransactionalService {@Autowiredprivate SysDeptMapper deptMapper;@Autowiredprivate SysConfigMapper configMapper;@Overridepublic int insertDept(SysDept dept) {dept.setAncestors("123123");deptMapper.insertDept(dept);TestTransactionalService service = Objects.nonNull(AopContext.currentProxy()) ? (TestTransactionalService)AopContext.currentProxy() : this;service.insertConfig();return 1;}@Override@Transactional(rollbackFor = Exception.class)public int insertConfig() {SysConfig config;for (int i = 0; i < 5; i++) {config = new SysConfig();config.setConfigName("配置" + i);configMapper.insertConfig(config);if (i == 2) {throw new RuntimeException();}}return 1;}
}

 

参照:https://www.cnblogs.com/fangniunanhai/p/15931432.html

springboot 事务自调用失效_自调用事务失效-CSDN博客

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com