您当前的位置:首页 > 电脑百科 > 程序开发 > 框架

Spring事务失效的各种场景

时间:2022-08-01 09:40:52  来源:  作者:是啊超ya

Spring事务失效的各种场景

一、访问权限

JAVA的访问权限主要是:private、default、protected、public,它们的权限则是依次变大。
如果我们在开发的时候定义错误的访问权限,就会导致事务出现问题

@Service
public class DemoService {
    
    @Transactional
    private void query(Demo demo) {

    }
}

 

我们可以查看源码,可以明白,spring事务的实现
AbstractFallbackTransactionAttributeSource类的computeTransactionAttribute方法中有个判断,如果目标方法不是public,则TransactionAttribute返回null,即不支持事务。

二、方法用final修饰

我们在学习的时候都知道,某个方法不想被子类继承的时候就会加上关键字final,这种写法正常来说是没有问题的,但是如果该方法被用上事务就不行了

@Service
public class DemoService {

    @Transactional
    public final void query(Demo demo) {
      
    }
}

这样的话会导致事务的失效,因为spring事务底层实现使用了代理,aop,通过jdk的动态代理或者cglib,生成了代理类,在代理类中实现了事务功能,如果方法被final修饰,无法重写该方法,也就无法添加事务的功能了


 

 

像idea这里就提出了提示,同样的如果方法是static方法也是不行的


 

 

因为静态方法是属于类的,而不是属于对象的,无法重写静态方法所以也就不可能实现事务

三、方法内部调用

在同一个类的service中,调用其他的事务方法

@Service
public class DemoService {

    @Transactional
    public  void query(Demo demo) {
        save(demo);
    }
    
    @Transactional
    public void save(Demo demo) {
        
    }
    
}

可以看到,query方法调用了save的方法,由于spring的事务实现是因为aop生成代理,这样是直接调用了this对象,所以也不会生成事务。

解决方法:
1、增加一个service,把一个事务的方法移到新增加的service方法里面,然后进行注入再调用

@Service
public class DemoTwoService {
    
    @Transactional
    public void save(Demo demo) {

    }
}

@Service
public class DemoService {

    @Autowired
    DemoTwoService demoTwoService;

    @Transactional
    public  void query(Demo demo) {
        demoTwoService.save(demo);
    }
}

2、在自己类中注入自己

@Service
public class DemoService {

    @Autowired
    DemoService demoService;

    @Transactional
    public  void query(Demo demo) {
        demoService.save(demo);
    }

    @Transactional
    public void save(Demo demo) {

    }
}

由于这种写法基于spring的三级缓存不会导致,循环依赖的问题出现

3、通过AopContentent

@Service
public class DemoService {
    
    @Transactional
    public  void query(Demo demo) {
        DemoService demoService = (DemoService)AopContext.currentProxy();
        demoService.save(demo);
    }

    @Transactional
    public void save(Demo demo) {

    }
}

四、没有被spring管理

在使用spring事务的时候,对象要被spring进行管理,也就是需要创建bean,一般我们都会加@Controller、@Service、@Component、@Repository等注解,可以自动实现bean实例化和依赖注入的功能。,如果忘记加了,也会导致,事务的失效

五、多线程调用

@Service
public class DemoService {

    @Autowired
    DemoTwoService demoTwoService;

    @Transactional
    public  void query(Demo demo) {
        new Thread(()->{
            demoTwoService.save(demo);
        }).start();
    }
}

通过上面的例子,我们可以知道,query调用了事务方法save,但是事务方法在另一个线程里面调用,这样会导致两个方法在不同的一个线程中,获取的数据库连接也不一样,所以会是两个不同的事务,如果save的方法抛出了异常,query回滚是不可能的,看过spring源码,我们可以知道,spring的事务是通过连接数据库来实现的,当前线程保存了一个map,key—数据源,value----数据库连接,事务其实就是指向同一个连接的,只有拥有同一个数据库连接才能同时提交和回滚,如果在不同的线程,数据库的连接不是同一个,所以事务也不是同一个。

private static final ThreadLocal<Map<Object, Object>> resources =

  new NamedThreadLocal<>("Transactional resources");

六、设计的表不支持事务

我们都知道,MySQL5之前默认的数据库引擎是MyISAM,可能一些老的项目还在使用,但是他是不支持事务的

七、没有开启事务

如果创建的不是springboot项目可能会导致这样的问题出现,因为springboot项目有自动装配的类
DataSourceTransactionManagerAutoConfiguration,已经默认开启了事务,配置spring.datasource参数就行,如果是spring项目,需要在ApplicationContext.xml配置的,不然事务不会生效

<!-- 配置事务管理器 --> 
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager"> 
    <property name="dataSource" ref="dataSource"></property> 
</bean> 
<tx:advice id="advice" transaction-manager="transactionManager"> 
    <tx:attributes> 
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes> 
</tx:advice> 
<!-- 用切点把事务切进去 --> 
<aop:config> 
    <aop:pointcut expression="execution(* com.demo.*.*(..))" id="pointcut"/> 
    <aop:advisor advice-ref="advice" pointcut-ref="pointcut"/> 
</aop:config> 

还有就是切点配错,也会导致事务失效

八、错误的事务传播

我们在使用@Transactional注解时,是可以指定propagation参数的。
该参数的作用是指定事务的传播特性,
spring目前支持7种传播特性:

• REQUIRED 如果当前上下文中存在事务,那么加入该事务,如果不存在事务,创建一个事务,这是默认的传播属性值。
• SUPPORTS 如果当前上下文存在事务,则支持事务加入事务,如果不存在事务,则使用非事务的方式执行。
• MANDATORY 如果当前上下文中存在事务,否则抛出异常。
• REQUIRES_NEW 每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。
• NOT_SUPPORTED 如果当前上下文中存在事务,则挂起当前事务,然后新的方法在没有事务的环境中执行。
• NEVER 如果当前上下文中存在事务,则抛出异常,否则在无事务环境上执行代码。
• NESTED 如果当前上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。
如果我们在手动设置propagation参数的时候,把传播特性设置错了

@Service
public class DemoService {

    @Transactional(propagation = Propagation.NEVER)
    public  void query(Demo demo) {

    }
}

我们可以看到query的事务传播设置为了Propagation.NEVER,这种类型的传播不支持事务,会抛异常,
目前支持事务的三种传播特性为:REQUIRED,REQUIRES_NEW,NESTED

九、自己捕获了异常

事务不回滚,可能是我们在写代码的时候自己在代码手动进行了try…catch

@Transactional
    public  void query(Demo demo) {
        try {
            save(demo);
        } catch (Exception e) {
            System.out.println("异常");
        }
    }

这种情况下,spring事务不会进行回滚,因为我们进行了手动捕获异常,然后没有手动抛出,如果想要spring事务的正常回滚,必须抛出它能处理的异常,如果没有抛出异常,spring会认为程序没有问题。

十、手动抛出别的异常

我们没有手动捕获异常,但是如果抛出的异常不正确,spring事务也不会回滚。

 @Transactional
    public  void query(Demo demo) throws Exception{
        try {
            save(demo);
        } catch (Exception e) {
            throw new Exception(e);
        }
    }

上面我们捕获了异常,然后手动抛出Exception,事务同样不会回滚,因为spring事务,默认情况下不会回滚Exception(非运行时的异常),只会回滚RuntimeException(运行时异常)和Error(错误)。

十一、自定义回滚异常

在使用@Transactional注解声明事务时,有时我们想自定义回滚的异常,spring也是支持的。可以通过设置rollbackFor参数,来完成这个功能。

但如果这个参数的值设置错了,就会引出一些问题

  @Transactional(rollbackFor = BusinessException.class)
    public  void query(Demo demo) throws Exception{
            save(demo);
    }

上面是我们自定义的业务异常,如果在执行上面这段代码,保存和更新数据时,程序报错了,抛了SqlException、DuplicateKeyException等异常。而BusinessException是我们自定义的异常,报错的异常不属于BusinessException,所以事务也不会回滚。即使rollbackFor有默认值,但阿里巴巴开发者规范中,还是要求开发者重新指定该参数。
因为如果使用默认值,一旦程序抛出了Exception,事务不会回滚,这会出现很大的bug。所以,建议一般情况下,将该参数设置成:Exception或Throwable。

十二、嵌套事务回滚过头

@Service
public class DemoService {

    @Autowired
    private DemoTwoService demoTwoService;

    @Autowired
    private DemoDao demoDao;
    public
    @Transactional(rollbackFor = Exception.class)
    public  void query(Demo demo) throws Exception{
        demoDao.save(demo);
        demoTwoService.save(demo);
    }
}

原本只是希望回滚demoServise.save(),不回滚demoDao.save(demo);,但是这种情况两个都会被回滚,因为demoTwoService.save(demo);没有捕获异常,往上抛出,导致query进行回滚,所以同时回滚了两个
解决方法:

@Service
public class DemoService {

    @Autowired
    private DemoTwoService demoTwoService;

    @Autowired
    private DemoDao demoDao;
    public
    @Transactional(rollbackFor = Exception.class)
    public  void query(Demo demo) throws Exception{
        demoDao.save(demo);
        try {
            demoTwoService.save(demo);
        } finally {
            
        }
    }
}

可以将内部嵌套事务放在try/catch中,并且不继续往上抛异常。这样就能保证,如果内部嵌套事务中出现异常,只回滚内部事务,而不影响外部事务。

十三、编程式事务

上面的事务失效都是基于@Transactional注解的,我们把这种事务叫做:声明式事务。

其实,spring还提供了另外一种创建事务的方式,即通过手动编写代码实现的事务,我们把这种事务叫做:编程式事务。

@Service
public class DemoService {
    

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Transactional(rollbackFor = Exception.class)
    public  void query(Demo demo) throws Exception{
       transactionTemplate.execute((status -> {
           save(demo);
           return Boolean.TRUE;
       }));
    }

    @Transactional
    public void save(Demo demo) {

    }
}

在spring中为了支持编程式事务,专门提供了一个类:TransactionTemplate,在它的execute方法中,就实现了事务的功能。

使用TransactionTemplate的编程式事务可以

• 避免由于spring aop问题,导致事务失效的问题。
• 能够更小粒度的控制事务的范围。
tips:关于本文举例的query加事务不太符合开发的要求,但是有点懒就不改了



Tags:Spring   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
Spring Security:保障应用安全的利器
SpringSecurity作为一个功能强大的安全框架,为Java应用程序提供了全面的安全保障,包括认证、授权、防护和集成等方面。本文将介绍SpringSecurity在这些方面的特性和优势,以及它...【详细内容】
2024-02-27  Search: Spring  点击:(62)  评论:(0)  加入收藏
Spring Security权限控制框架使用指南
在常用的后台管理系统中,通常都会有访问权限控制的需求,用于限制不同人员对于接口的访问能力,如果用户不具备指定的权限,则不能访问某些接口。本文将用 waynboot-mall 项目举例...【详细内容】
2024-02-19  Search: Spring  点击:(43)  评论:(0)  加入收藏
详解基于SpringBoot的WebSocket应用开发
在现代Web应用中,实时交互和数据推送的需求日益增长。WebSocket协议作为一种全双工通信协议,允许服务端与客户端之间建立持久性的连接,实现实时、双向的数据传输,极大地提升了用...【详细内容】
2024-01-30  Search: Spring  点击:(23)  评论:(0)  加入收藏
Spring实现Kafka重试Topic,真的太香了
概述Kafka的强大功能之一是每个分区都有一个Consumer的偏移值。该偏移值是消费者将读取的下一条消息的值。可以自动或手动增加该值。如果我们由于错误而无法处理消息并想重...【详细内容】
2024-01-26  Search: Spring  点击:(95)  评论:(0)  加入收藏
SpringBoot如何实现缓存预热?
缓存预热是指在 Spring Boot 项目启动时,预先将数据加载到缓存系统(如 Redis)中的一种机制。那么问题来了,在 Spring Boot 项目启动之后,在什么时候?在哪里可以将数据加载到缓存系...【详细内容】
2024-01-19  Search: Spring  点击:(91)  评论:(0)  加入收藏
Spring Boot2.0深度实践 核心原理拆解+源码分析
Spring Boot2.0深度实践:核心原理拆解与源码分析一、引言Spring Boot是一个基于Java的轻量级框架,它简化了Spring应用程序的创建过程,使得开发者能够快速搭建一个可运行的应用...【详细内容】
2024-01-15  Search: Spring  点击:(101)  评论:(0)  加入收藏
SpringBoot3+Vue3 开发高并发秒杀抢购系统
开发高并发秒杀抢购系统:使用SpringBoot3+Vue3的实践之旅随着互联网技术的发展,电商行业对秒杀抢购系统的需求越来越高。为了满足这种高并发、高流量的场景,我们决定使用Spring...【详细内容】
2024-01-14  Search: Spring  点击:(94)  评论:(0)  加入收藏
Spring Boot 3.0是什么?
Spring Boot 3.0是一款基于Java的开源框架,用于简化Spring应用程序的构建和开发过程。与之前的版本相比,Spring Boot 3.0在多个方面进行了改进和增强,使其更加易用、高效和灵活...【详细内容】
2024-01-11  Search: Spring  点击:(140)  评论:(0)  加入收藏
GraalVM与Spring Boot 3.0:加速应用性能的完美融合
在2023年,SpringBoot3.0的发布标志着Spring框架对GraalVM的全面支持,这一支持是对Spring技术栈的重要补充。GraalVM是一个高性能的多语言虚拟机,它提供了Ahead-of-Time(AOT)编...【详细内容】
2024-01-11  Search: Spring  点击:(135)  评论:(0)  加入收藏
Spring Boot虚拟线程的性能还不如Webflux?
早上看到一篇关于Spring Boot虚拟线程和Webflux性能对比的文章,觉得还不错。内容较长,抓重点给大家介绍一下这篇文章的核心内容,方便大家快速阅读。测试场景作者采用了一个尽可...【详细内容】
2024-01-10  Search: Spring  点击:(135)  评论:(0)  加入收藏
▌简易百科推荐
Qt与Flutter:在跨平台UI框架中哪个更受欢迎?
在跨平台UI框架领域,Qt和Flutter是两个备受瞩目的选择。它们各自具有独特的优势,也各自有着广泛的应用场景。本文将对Qt和Flutter进行详细的比较,以探讨在跨平台UI框架中哪个更...【详细内容】
2024-04-12  刘长伟    Tags:UI框架   点击:(7)  评论:(0)  加入收藏
Web Components实践:如何搭建一个框架无关的AI组件库
一、让人又爱又恨的Web ComponentsWeb Components是一种用于构建可重用的Web元素的技术。它允许开发者创建自定义的HTML元素,这些元素可以在不同的Web应用程序中重复使用,并且...【详细内容】
2024-04-03  京东云开发者    Tags:Web Components   点击:(11)  评论:(0)  加入收藏
Kubernetes 集群 CPU 使用率只有 13% :这下大家该知道如何省钱了
作者 | THE STACK译者 | 刘雅梦策划 | Tina根据 CAST AI 对 4000 个 Kubernetes 集群的分析,Kubernetes 集群通常只使用 13% 的 CPU 和平均 20% 的内存,这表明存在严重的过度...【详细内容】
2024-03-08  InfoQ    Tags:Kubernetes   点击:(23)  评论:(0)  加入收藏
Spring Security:保障应用安全的利器
SpringSecurity作为一个功能强大的安全框架,为Java应用程序提供了全面的安全保障,包括认证、授权、防护和集成等方面。本文将介绍SpringSecurity在这些方面的特性和优势,以及它...【详细内容】
2024-02-27  风舞凋零叶    Tags:Spring Security   点击:(62)  评论:(0)  加入收藏
五大跨平台桌面应用开发框架:Electron、Tauri、Flutter等
一、什么是跨平台桌面应用开发框架跨平台桌面应用开发框架是一种工具或框架,它允许开发者使用一种统一的代码库或语言来创建能够在多个操作系统上运行的桌面应用程序。传统上...【详细内容】
2024-02-26  贝格前端工场    Tags:框架   点击:(52)  评论:(0)  加入收藏
Spring Security权限控制框架使用指南
在常用的后台管理系统中,通常都会有访问权限控制的需求,用于限制不同人员对于接口的访问能力,如果用户不具备指定的权限,则不能访问某些接口。本文将用 waynboot-mall 项目举例...【详细内容】
2024-02-19  程序员wayn  微信公众号  Tags:Spring   点击:(43)  评论:(0)  加入收藏
开发者的Kubernetes懒人指南
你可以将本文作为开发者快速了解 Kubernetes 的指南。从基础知识到更高级的主题,如 Helm Chart,以及所有这些如何影响你作为开发者。译自Kubernetes for Lazy Developers。作...【详细内容】
2024-02-01  云云众生s  微信公众号  Tags:Kubernetes   点击:(58)  评论:(0)  加入收藏
链世界:一种简单而有效的人类行为Agent模型强化学习框架
强化学习是一种机器学习的方法,它通过让智能体(Agent)与环境交互,从而学习如何选择最优的行动来最大化累积的奖励。强化学习在许多领域都有广泛的应用,例如游戏、机器人、自动驾...【详细内容】
2024-01-30  大噬元兽  微信公众号  Tags:框架   点击:(72)  评论:(0)  加入收藏
Spring实现Kafka重试Topic,真的太香了
概述Kafka的强大功能之一是每个分区都有一个Consumer的偏移值。该偏移值是消费者将读取的下一条消息的值。可以自动或手动增加该值。如果我们由于错误而无法处理消息并想重...【详细内容】
2024-01-26  HELLO程序员  微信公众号  Tags:Spring   点击:(95)  评论:(0)  加入收藏
SpringBoot如何实现缓存预热?
缓存预热是指在 Spring Boot 项目启动时,预先将数据加载到缓存系统(如 Redis)中的一种机制。那么问题来了,在 Spring Boot 项目启动之后,在什么时候?在哪里可以将数据加载到缓存系...【详细内容】
2024-01-19   Java中文社群  微信公众号  Tags:SpringBoot   点击:(91)  评论:(0)  加入收藏
站内最新
站内热门
站内头条