spring Transactional

文章详细介绍了如何使用Spring的@Transactional注解进行事务管理,包括注解的位置、注意事项和使用方式。强调了注解应应用于public方法,避免在类级别使用,以及事务的传播行为(如REQUIRED、REQUIRES_NEW等)和事务隔离级别的概念,如Serializable、ReadCommitted等,以防止数据不一致问题。
摘要由CSDN通过智能技术生成

1、使用位置

接口实现类或接口实现方法上,而不是接口类中。

访问权限:public 的方法才起作用。@Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。

系统设计:将标签放置在需要进行事务管理的方法上,而不是放在所有接口实现类上:只读的接口就不需要事务管理,由于配置了@Transactional就需要AOP拦截及事务的处理,可能影响系统性能

2、注意点

1) 不要在类上标注Transactional注解,要在需要的方法上标注。即使类的每个方法都需要事务也不要在类上标注,因为有可能你或别人新添加的方法根本不需要事务。

2) 标注了Transactional注解的方法体中不要涉及耗时很久的操作,如IO操作、网络通信等。

3) 根据业务需要设置合适的事务参数,如是否需要新事务(事务的传播)、超时时间等。

3、使用姿势

3.1 单个方法

一般不需要在业务方法中catch异常,如果非要catch,在做完你想做的工作后(比如关闭文件等)一定要抛出runtime exception,否则spring会将你的操作commit,这样就会产生脏数据.所以你的catch代码是画蛇添足。

@Override
@Transactional(rollbackFor = Exception.class)
public Json addOrder(TOrderAddReq tOrderAddReq) {
 try{
       //do 增删改方法
    } catch (Exception e) { 
        //有异常直接抛出         
        throw e;           
    }
    return json;
}

3.2 try catch 影响

结论一:对于@Transactional可以保证RuntimeException错误的回滚,如果想保证非RuntimeException错误的回滚,需要加上rollbackFor = Exception.class 参数。

结论二:try catch只是对异常是否可以被@Transactional 感知 到有影响。如果错误抛到切面可以感知到的地步,那就可以起作用。

3.3 事务嵌套

嵌套事务,实际是指的spring事务的传播机制

@GetMapping("/xxx")
@Transactional
public void doXxx() throws Exception{
    innerService.inner();
    int age = random.nextInt(100);
    User user = new User().setAge(age).setName("name:" + age);
    userService.save(user);
    throw new Exception();
}
 
@Transactional( rollbackFor = Exception.class)
public void inner() throws Exception{
    //do 增删改操作
}

Transactional 注解中的事务传播默认设置如下:

//如果发现已经有事务存在了,就加入这个事务,而不是新建一个事务
Propagation propagation() default Propagation.REQUIRED;

使用小结:

结论一:
对于@Transactional可以保证RuntimeException错误的回滚,如果想保证非RuntimeException错误的回滚,需要加上rollbackFor = Exception.class 参数。
结论二:
try catch只是对异常是否可以被@Transactional 感知 到有影响。如果错误抛到切面可以感知到的地步,那就可以起作用。
结论三:
由于REQUIRED属性,“两个事务”其实是一个事务,处理能力看报错时刻,是否添加了处理非RuntimeException的能力。

附录:

几种事务失效的场景

上面说到的两个问题,其实就是@Transactional注解使用不当,导致失效的两种情形;除此之外,以下几种情况也会导致事务失效:

  • 业务代码中存在异常时,使用try…catch…语句块捕获,而catch语句块没有throw new RuntimeExecption异常;(最难被排查到问题且容易忽略)

  • 注解@Transactional中Propagation属性值设置错误即Propagation.NOT_SUPPORTED(一般不会设置此种传播机制)

  • mysql关系型数据库,且存储引擎是MyISAM而非InnoDB,则事务会不起作用(比较少见);

  • 业务代码抛出异常类型非RuntimeException,事务失效;Spring默认抛出未检查unchecked异常(继承自 RuntimeException 的异常)或者 Error才回滚事务;其他异常不会触发回滚事务。如果在事务中抛出其他类型的异常,但却期望 Spring 能够回滚事务,就需要指定 rollbackFor属性

事务的传播行为

事务的传播行为也会影响到事务与事务之间的关系,一定要搞清楚,否则经常会出现很奇怪的问题。

具体来讲有以下几种属性:

  • propagation 代表事务的传播行为,默认值为 Propagation.REQUIRED,其他的属性信息如下:

  • Propagation.REQUIRED:如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。( 也就是说如果A方法和B方法都添加了注解,在默认传播模式下,A方法内部调用B方法,会把两个方法的事务合并为一个事务 )

  • Propagation.SUPPORTS:如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。

  • Propagation.MANDATORY:如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。

  • Propagation.REQUIRES_NEW:重新创建一个新的事务,如果当前存在事务,暂停当前的事务。( 当类A中的 a 方法用默Propagation.REQUIRED模式,类B中的 b方法加上采用 Propagation.REQUIRES_NEW模式,然后在 a 方法中调用 b方法操作数据库,然而 a方法抛出异常后,b方法并没有进行回滚,因为Propagation.REQUIRES_NEW会暂停 a方法的事务 )

  • Propagation.NOT_SUPPORTED:以非事务的方式运行,如果当前存在事务,暂停当前的事务。

  • Propagation.NEVER:以非事务的方式运行,如果当前存在事务,则抛出异常。

  • Propagation.NESTED :和 Propagation.REQUIRED 效果一样。

事务的隔离级别

SQL标准定义了4种事务隔离级别来避免3种数据不一致的问题。事务等级从高到低,分别为:

1.Serializable(序列化)

系统中所有的事务以串行地方式逐个执行,所以能避免所有数据不一致情况。

但是这种以排他方式来控制并发事务,串行化执行方式会导致事务排队,系统的并发量大幅下降,使用的时候要绝对慎重。

2.Repeatable read(可重复读)

一个事务一旦开始,事务过程中所读取的所有数据不允许被其他事务修改。

一个隔离级别没有办法解决“幻影读”的问题。

因为它只“保护”了它读取的数据不被修改,但是其他数据会被修改。如果其他数据被修改后恰好满足了当前事务的过滤条件(where语句),那么就会发生“幻影读”的情况。

其他两种事务隔离等级为:

3.Read Committed(已提交读)

一个事务能读取到其他事务提交过(Committed)的数据。

一个事务在处理过程中如果重复读取某一个数据,而且这个数据恰好被其他事务修改并提交了,那么当前重复读取数据的事务就会出现同一个数据前后不同的情况。

在这个隔离级别会发生“不可重复读”的场景。

4.Read Uncommitted(未提交读)

一个事务能读取到其他事务修改过,但是还没有提交的(Uncommitted)的数据。

数据被其他事务修改过,但还没有提交,就存在着回滚的可能性,这时候读取这些“未提交”数据的情况就是“脏读”。

在这个隔离级别会发生“脏读”场景。

参考:

spring事务注解@Transactional与trycatch
https://www.jb51.net/article/215488.htm
spring  @Transactional踩坑记
https://juejin.cn/post/7203989318271090744

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值