Spring注解事务诡异提交全面解析

简介: 一、问题产生背景应用上线的时候,正常调用Tomcat的shutdown.sh脚本,事务执行一半异常提交。伪代码如下:@Override @Transactional(propagation = Propagation.

一、问题产生背景

应用上线的时候,正常调用Tomcat的shutdown.sh脚本,事务执行一半异常提交。伪代码如下:

@Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void insert(PaymentOrder paymentOrder) {
        try{
            paymentOrderDao.update(paymentOrder);
            PaymentOrderDao.insert(paymentOrder)
        }catch(Exception e){
            logger.error(" 操作支付订单失败 biz " + paymentOrder.getBiz() + " bizOrder " + paymentOrder.getBizOrder(), e);
            Throw e;
        }
    }

上面是一段伪代码,实际在tomcat重启的时候,上面update语句提交,而insert没有。

二、思路解析

1、直接将Tomcat服务kill掉能否重现问题
按之前的理解是,Tomcat重启事务中断,数据库在事务连接超时后会回滚事务。那么写一段代码试一下,使用Kill -9命令中断tomcat服务后发现数据库事务竟然回滚了。

2、分析Tomcat的shutdown.sh命令
其实shutdown.sh命令最终调用的是catalina.sh命令脚本,看sh源码如下:

exec "$_RUNJAVA" $JAVA_OPTS $CATALINA_OPTS \
    -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \
    -Dcatalina.base="$CATALINA_BASE" \
    -Dcatalina.home="$CATALINA_HOME" \
    -Djava.io.tmpdir="$CATALINA_TMPDIR" \
    org.apache.catalina.startup.Bootstrap "$@" stop

其实最终是调用的Bootstrap这个类来关闭服务的,我们再来看这个类的内容。

if (server instanceof Lifecycle) { 
            try { 
               ((Lifecycle) server).stop(); 
            } catch (LifecycleException e) { 
                log.error("Catalina.stop", e); 
            } 
        }

Tomcat是将注册进来的服务循环逐个关闭,这时候在关闭的时候可能会因为前一个资源关闭而造成后一个资源抛出异常,注意这个异常有可能是Throwable,也可能是Exception,后面详细解释。

3、分析Spring注解事务源码

Paste_Image.png

在Tomcat关闭的时候,抛出的异常和上面代码的异常没有匹配成功,spring异常匹配采用迭代当前异常的所有父类与目标异常匹配,匹配不到后检查当前异常是否为Error或者RuntimeException的实例,是的话也能匹配上,但是没有匹配是否为Throwable的实例

三、问题总结

通过上面的问题分析,可以把代码写成如下样式:

@Override
    @Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRED)
    public void insert(PaymentOrder paymentOrder) {
        try{
            paymentOrderDao.update(paymentOrder);
            PaymentOrderDao.insert(paymentOrder)
        }catch(Throwable e){
            logger.error(" 操作支付订单失败 biz " + paymentOrder.getBiz() + " bizOrder " + paymentOrder.getBizOrder(), e);
            Throw e;
        }
    }

采用Throwable捕获方能确保Tomcat异常重启,事务能够正确回滚。

目录
相关文章
|
6天前
|
运维 Java 程序员
Spring5深入浅出篇:基于注解实现的AOP
# Spring5 AOP 深入理解:注解实现 本文介绍了基于注解的AOP编程步骤,包括原始对象、额外功能、切点和组装切面。步骤1-3旨在构建切面,与传统AOP相似。示例代码展示了如何使用`@Around`定义切面和执行逻辑。配置中,通过`@Aspect`和`@Around`注解定义切点,并在Spring配置中启用AOP自动代理。 进一步讨论了切点复用,避免重复代码以提高代码维护性。通过`@Pointcut`定义通用切点表达式,然后在多个通知中引用。此外,解释了AOP底层实现的两种动态代理方式:JDK动态代理和Cglib字节码增强,默认使用JDK,可通过配置切换到Cglib
|
2天前
|
JSON 前端开发 Java
【JAVA进阶篇教学】第七篇:Spring中常用注解
【JAVA进阶篇教学】第七篇:Spring中常用注解
|
4天前
|
SQL Java 关系型数据库
Spring 事务
Spring 事务
7 1
|
5天前
|
JavaScript Java 开发者
Spring Boot中的@Lazy注解:概念及实战应用
【4月更文挑战第7天】在Spring Framework中,@Lazy注解是一个非常有用的特性,它允许开发者控制Spring容器的bean初始化时机。本文将详细介绍@Lazy注解的概念,并通过一个实际的例子展示如何在Spring Boot应用中使用它。
17 2
|
6天前
|
前端开发 Java
SpringBoot之自定义注解参数校验
SpringBoot之自定义注解参数校验
16 2
|
6天前
|
Java 数据库连接 数据库
Spring事务简介,事务角色,事务属性
Spring事务简介,事务角色,事务属性
16 2
|
10天前
|
Java 数据库连接 数据库
16:事务-Java Spring
16:事务-Java Spring
26 5
|
12天前
|
消息中间件 Java 关系型数据库
Spring事务与分布式事务
这篇文档介绍了事务的概念和数据库事务的ACID特性:原子性、一致性、隔离性和持久性。在并发环境下,事务可能出现更新丢失、脏读和不可重复读等问题,这些问题通过设置事务隔离级别(如读未提交、读已提交、可重复读和序列化)来解决。Spring事务传播行为有七种模式,影响嵌套事务的执行方式。`@Transactional`注解用于管理事务,其属性包括传播行为、隔离级别、超时和只读等。最后提到了分布式事务,分为跨库和跨服务两种情况,跨服务的分布式事务通常通过最终一致性策略,如消息队列实现。
|
12天前
|
Java Spring
springboot自带的@Scheduled注解开启定时任务
springboot自带的@Scheduled注解开启定时任务
|
13天前
|
监控 Java 测试技术
Spring Boot与事务钩子函数:概念与实战
【4月更文挑战第29天】在复杂的业务逻辑中,事务管理是确保数据一致性和完整性的关键。Spring Boot提供了强大的事务管理机制,其中事务钩子函数(Transaction Hooks)允许开发者在事务的不同阶段插入自定义逻辑。本篇博客将详细探讨事务钩子函数的概念及其在Spring Boot中的应用。
35 1

推荐镜像

更多