声明式事务失效的几种场景

一、抛出的异常类型不匹配 

默认情况下,如果不特殊声明,spring 事务只有遇到 RuntimeException 及 Error 时才会回滚。

org.springframework.transaction.interceptor.TransactionAspectSupport.completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex):

if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {……}

org.springframework.transaction.interceptor.DefaultTransactionAttribute.rollbackOn(Throwable):

public boolean rollbackOn(Throwable ex) {
    return (ex instanceof RuntimeException || ex instanceof Error);
}

如果需要,可以使用rollbackFor来进行异常类型的声明,例如:rollbackFor = Exception.class 

org.springframework.transaction.annotation.Transactional:

注:即使声明rollbackFor = Exception.class ,遇到Error时,事务依然生效


二、非public修饰的/ final、static 修饰的方法

事务底层使用的是基于 AOP的代理模式,代理模式生成的代理类无法重写被 final、static 修饰的方法,也无法访问private方法

简单来说代理类似于:

class 普通类 {
    public void a() {……}
}

class 代理类 extends 普通类 {
    // 重写普通类方法
    public void a() {
        // 事务逻辑代码
            ……
        // 原类方法
        super.a();
    }
}

注:同类中有事务声明的方法a,调用了private方法b,方法b发生异常后,a和b都能正常回滚,但这是因为方法b在方法a的事务中,为同一个事务,而非方法b本身声明的事务生效:

@Transactional
public void a() {
    b();
}

@Transactional
private void b() {
    
}

三、类内部访问

如没有事务声明的方法a,调用了有事务声明的方法b,这时调用方法b的不是代理对象,而是普通对象,由上述,普通对象不存在事务增强逻辑,这就导致了方法b的事务失效

public void a() {
    b();
}

@Transactional
public void b() {
    
}

可通过@Autowired注解注入自身类的代理对象,再调用方法b

可将方法b放在别的类B中,在本类中注入类B的代理对象,再调用方法b

可使用AopContext.currentProxy()获取代理对象,再调用方法b


四、多线程

Spring的事务是通过数据库连接来创建,数据库连接底层是放在ThreadLocal,在单线程下,相同数据源的数据库连接是同一个。而多线程场景下,拿到的数据库连接并不一样,会导致获取到不同的事务,没办法进行统一回滚。

可以使用编程式事务进行手动处理


五、使用错误的传播行为

若指定成了NOT_SUPPORTED、NEVER这类以非事务方式执行的事务传播机制,则事务不生效

@Transactional(propagation = Propagation.NOT_SUPPORTED)


六、异常被吞

catch了异常,进行处理后,没有再次抛出异常,如:

try {
    ……
} catch (Exception e) {
    e.printStackTrace();
}

七、bean没有被Spring管理

如没有@Component这类注解


八、数据库不支持事务

如Mysql的MyISAM存储引擎不支持事务


九、命名不符合

使用配置文件声明的,最终使用时,类名和方法名跟配置文件中声明的规范不符合

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值