N

Norlcayn的博客

A text-focused Halo theme

  • 首页
  • 文章
  • 默认分类
  • 关于
主页 事务失效
文章

事务失效

发表于 2026-01-16 更新于 昨天
作者 Administrator
14~18 分钟 阅读

事务失效问题

如果只对其中的一段代码添加事务,比方只对其中的**this.save(orders);**一行添加事务

想实现的话有两种方案:

  1. 使用Spring的编程式事务,这种方式控制粒度更精准,但是缺点是代码耦合度高,需要自己写代码处理事务
  2. 将需要控制事务的那段代码单独提到一个方法中去,然后仅仅对新抽取的方法控制事务
@Override
public PlaceOrderResDTO placeOrder(PlaceOrderReqDTO placeOrderReqDTO) {
    //... 省略代码
    this.saveOrders(orders);
    //... 省略代码
}

//新编写一个方法,将需要事务控制事务的方法,提到这个位置来
@Transactional
public void saveOrders(Orders orders){
    this.save(orders);
}

上面的方法可以很好的解决长事务的问题,但是它也引入了一个新的问题,那就是事务失效

@Transactional
public void saveOrders(Orders orders){
    this.save(orders);

    //手动模拟一个异常,看看事务会不会回滚
    int i = 1 / 0;
}

解决方案

运行上面的代码,会发现事务不再自动回滚,原因很简单:那就是这个方法的调用者是原始对象,不是代理对象

而原始对象是没有事务控制能力的,那解决方案就是将调用者再想办法还原为代理对象

@Autowired
private IOrdersCreateService owner; 

@Override
public PlaceOrderResDTO placeOrder(PlaceOrderReqDTO placeOrderReqDTO) {
    //... 省略代码
    owner.saveOrders(orders);
    //... 省略代码
}

@Transactional
public void saveOrders(Orders orders){
    this.save(orders);

    //模拟异常
    //int i =  1 / 0;
}

接口中也需要对应的暴露新增的方法

/**
 * 保存订单
 *
 * @param orders 订单
 */
void saveOrders(Orders orders);

重新测试,事务问题得到解决

总结

事务失效的情况有哪些?

  • 非事务方法内部调用事务方法
  • 事务方法没有使用public修饰
  • 事务方法的异常在方法内被捕获处理
  • 事务方法抛出的异常与rollbackFor属性指定的异常不匹配
  • 事务方法配置的事务传播行为有误

① 非事务方法内部调用事务方法

在下面方法中,insertOrderAndReduceStock()方法使用的是原始对象调用的,而不是代理对象调用的

而事务管理功能是代理对象负责的,因此事务失效!

@Service
public class OrderService {

    public void createOrder(){
        // ... 准备订单数据
        // 生成订单并扣减库存
        insertOrderAndReduceStock();
    }

    @Transactional
    public void insertOrderAndReduceStock(){
        // 生成订单
        insertOrder();
        // 扣减库存
        reduceStock();
    }
}

② 事务方法没有使用public修饰

Spring的声明式事务是基于AOP方式结合动态代理来实现的,在其内部有一个类

AbstractFallbackTransactionAttributeSource会检查方法是否使用public修饰,如果不是,则不能进行功能增强

在下面方法中,createOrder()方法没有使用public修饰,因此无法进行事务增强,事务失效!

@Service
public class OrderService {

    @Transactional
    private void createOrder(){
        // ... 准备订单数据
        // 生成订单
        insertOrder();
        // 扣减库存
        reduceStock();
    }
}

③ 事务方法的异常在方法内被捕获处理

在下面方法中,createOrder方法执行过程中即便出现了异常也不会向外抛出

而Spring的事务管理就是要感知业务方法的异常,当捕获到异常后才会回滚事务

现在事务被捕获,就会导致Spring无法感知事务异常,自然不会回滚,事务就失效了!

@Service
public class OrderService {
    @Transactional
    private void createOrder(){
        // ... 准备订单数据
        try {
            // 生成订单
            insertOrder();
            // 扣减库存
            reduceStock();
        } catch (Exception e) {
            // 处理异常
        }
    }
}

④ 事务方法抛出的异常与rollbackFor属性指定的异常不匹配

Spring的@Transactional使用rollbackFor属性指定当前事务管理器感知的异常类型(默认为RuntimeException)

下面方法createOrder方法抛出了一个IOException,不会被Spring捕获,事务就失效了!

@Service
 public class OrderService {

    @Transactional(rollbackFor = RuntimeException.class)
    public void createOrder() throws IOException {
        // ... 准备订单数据
        try {
            // 生成订单
            insertOrder();
            // 扣减库存
            reduceStock();
        } catch (Exception e) {
            // 处理异常
            throw new IOException();
        }
    }
 }

⑤ 事务方法配置的事务传播行为有误

下面方法reduceStock方法配置了事务传播行为Propagation.REQUIRES_NEW,他代表当前方法会开启一个新事物

也就是与createOrder和insertOrder不在同一个事物中了,因此事务失效!

@Service
public class OrderService1 {

    @Autowired
    private OrderService2 orderService2;

    @Transactional
    public void createOrder() {
        // 生成订单
        orderService2.insertOrder(); // 使用默认的Propagation.REQUIRED传播行为
        // 扣减库存
        orderService2.reduceStock();// 使用Propagation.REQUIRES_NEW传播行为
    }

}

@Service
public class OrderService2 {

    @Transactional // 默认传播行为:Propagation.REQUIRED
    public void insertOrder() {
        // 订单数据插入操作
    }

    //Propagation.REQUIRES_NEW 代表必须新事物
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void reduceStock() {
        // 库存扣减操作
    }
}
云岚到家相关技术栈
事务管理
许可协议:  CC BY 4.0
分享

相关文章

1月 16, 2026

状态机

什么是状态机 需求说明 在订单模块中订单共设计了7种状态,如下图 订单会在多个状态间进行转换,这就需要开发人员都去记忆这些转换关系,这是非常麻烦的 if(订单状态 == 待支付){ //如果用户支付成功,执行此场景下的业务逻辑,更新订单状态为派单中 update(id,派单中) ) if(

1月 16, 2026

数据表设计规范

设计范式 数据库设计三范式: 第一范式:数据表中的每一列都是原子的,不可再拆分 第二范式:每张表中的一条记录只描述一个实体 第三范式:数据表中不要存在冗余字段(可以通过其他字段推导出的字段) 1)第一范式:数据表中的每一列都是原子的,不可再拆分 有一张用户考勤表设计如下 id 用户名 考勤时间 1

1月 16, 2026

缓存优化

针对系统中访问频率很高的页面,常见的优化方案如下: 将页面做页面静态化处理 如果静态化之后依旧无法满足需求,可以将静态资源转移到CDN服务器 页面上变动的内容可以异步请求后端服务器获取数据,后端可以添加缓存来减轻数据库的压力 数据库方面可以通过合理的存储引擎选择、表字段设计、索引设计等进一步提高查询

下一篇

状态机

上一篇

订单重复提交问题

最近更新

  • Hello Halo
  • 状态机
  • 数据表设计规范
  • 缓存优化
  • 事务失效

热门标签

Halo 异常处理 Redisson 并行 Vue 并发 递归 请求限流 SpringAMQP 类加载器

目录

©2026 Norlcayn的博客. 保留部分权利。

使用 Halo 主题 Chirpy