Java面试题:Spring事务的失效场景

Spring事务的实现方式和原理

​ 在使用 Spring 框架的时候,事务的实现方式有两种,一种是编程式事务,程序员自己通过代码来控制事务的处理逻辑,还有一种是声明式事务,可以通过 @Transactional 注解来实现。

​ 其实事务的操作本来应该是由数据库来进行控制的,但是为了方便程序员进行业务逻辑的操作,Spring 对事务功能进行了扩展实现,一般我们很少会用到编程式事务,更多的是通过添加 @Transactional 注解来进行实现,由 Spring 框架来帮助进行控制。

​ 在一个方法上加了 @Transactional 注解后,Spring 会基于这个类生成一个代理对象,会将这个代理对象作为 bean,当使用这个代理对象的方法时,那么代理逻辑会先把事务的自动提交设置为 false,然后再去执行具体的业务逻辑,如果执行逻辑没有出现异常,那么代理逻辑就会将事务进行提交,如果执行逻辑出现了异常,那么则会将事务进行回滚。默认情况下会对 RuntimeExceptionError 进行回滚。可以利用 @Transactional 注解中的 rollbackFor 属性进行配置异常信息。

Spring事务失效的场景

1) 非 public 修饰的方法

When using proxies, you should apply the @Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods.

以上段落来自 Spring 官方文档,大概的意思就是 @Transactional 只能用于 public 的方法上,否则事务不会生效,如果要用在非 public 方法上,可以开启 AspectJ 代理模式。

2) 自身调用

@Service
public class OrderServiceImpl implements OrderService {

    public void update(Order order) {
        updateOrder(order);
    }
    
    @Transactional
    public void updateOrder(Order order) {
        // update order
    }
    
}

事务的管理是通过代理执行的方式生效的,如果是方法内部调用,没有经过 Spring 的代理类,就调用不到了

3)被 final、static 关键字修饰的类或方法

cglib 动态代理是通过生成目标类子类的方式生成代理类的,如果被final、static修饰后,无法继承父类与父类的方法。

4)没有被 Spring 管理

// @Service
public class OrderServiceImpl implements OrderService {

    @Transactional
    public void updateOrder(Order order) {
        // update order
    }
    
}

如果此时把 @Service 注解注释掉,这个类就不会被加载成一个 Bean,那这个类就不会被 Spring 管理了,事务自然就失效了。

5)数据库不支持事务

比如 Mysql 的 Myisam 存储引擎是不支持事务的,只有 innodb存储引擎才支持。

6)异常被捕获
@Service
public class OrderServiceImpl implements OrderService {
    @Transactional
    public void updateOrder(Order order) {
        try {
            // update order
        } catch {
 
        }
    }
}

当异常被捕获后,而且没有再抛出,那么事务是不会回滚的。

7)异常类型错误
@Service
public class OrderServiceImpl implements OrderService 
    @Transactional
    public void updateOrder(Order order) {
        try {
            // update order
        } catch {
            throw new Exception("更新错误");
        }
    }
}

这样事务也是不生效的,因为默认回滚的是:RuntimeExceptionError,如果想触发其他异常的回滚,需要在注解上配置一下,如通过指定@Transactional(rollbackFor = Exception.class)的方式进行全异常捕获。

8)多线程调用

Spring 的事务是通过数据库连接来实现,而数据库连接是放在 ThreadLocal 里面。同一个事务,只能用同一个数据库连接。而多线程场景下,拿到的数据库连接是不一样的。

9)错误的传播行为

使用的传播特性不支持事务

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public boolean save(User user) {
    boolean isSuccess = userService.save(user);
    try {
        int i = 1 / 0;
    } catch (Exception e) {
        throw new RuntimeException();
    }
    return isSuccess;
}
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值