@transactional

@transactional

@Transactional 是声明式事务管理 编程中使用的注解 基于aop面向切面编程 声明式事务分为两种一种基于TX和AOP的xml配置方式,二种就是基于@transcational注解

1.添加位置

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

  • 访问权限 public修饰的方法才起作用,

  • 作用于方法:当类配置了@Transactional,方法也配置了@Transactional,方法的事务会覆盖类的事务配置信息。

  • 作用于接口:不推荐这种使用方法,因为一旦标注在Interface上并且配置了Spring AOP 使用CGLib动态代理,将会导致@Transactional注解失效

@transcation的 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 效果一样。

spring 事务隔离级别

1、DEFAULT

默认隔离级别,每种数据库支持的事务隔离级别不一样,如果Spring配置事务时将isolation设置为这个值的话,那么将使用底层数据库的默认事务隔离级别。顺便说一句,如果使用的MySQL,可以使用"select @@tx_isolation"来查看默认的事务隔离级别

2、READ_UNCOMMITTED

读未提交,即能够读取到没有被提交的数据,所以很明显这个级别的隔离机制无法解决脏读、不可重复读、幻读中的任何一种,因此很少使用

3、READ_COMMITED

读已提交,即能够读到那些已经提交的数据,自然能够防止脏读,但是无法限制不可重复读和幻读

4、REPEATABLE_READ

重复读取,即在数据读出来之后加锁,类似"select * from XXX for update",明确数据读取出来就是为了更新用的,所以要加一把锁,防止别人修改它。REPEATABLE_READ的意思也类似,读取了一条数据,这个事务不结束,别的事务就不可以改这条记录,这样就解决了脏读、不可重复读的问题,但是幻读的问题还是无法解决

5、SERLALIZABLE

串行化,最高的事务隔离级别,不管多少事务,挨个运行完一个事务的所有子事务之后才可以执行另外一个事务里面的所有子事务,这样就解决了脏读、不可重复读和幻读的问题了

网上专门有图用表格的形式列出了事务隔离级别解决的并发问题:

img

再必须强调一遍,不是事务隔离级别设置得越高越好,事务隔离级别设置得越高,意味着势必要花手段去加锁用以保证事务的正确性,那么效率就要降低,因此实际开发中往往要在效率和并发正确性之间做一个取舍,一般情况下会设置为READ_COMMITED,此时避免了脏读,并发性也还不错,之后再通过一些别的手段去解决不可重复读和幻读的问题就好了。

脏读:脏读就是当一个事务正在访问数据,并对数据进行了修改,而这种修改还没有被提交到数据库中,这是我读到的数据还是之前的数据就产生了脏读

不可重复读:A 在一个事务内 多次进行读操作 另外一个B在修改数据 导致 A多次读到的数据不一致

幻读 :一个事务进行读取,分别读取到了不同的数据。需要注意的是这里针对的是数据条数,可以理解为针对多笔数据是个数据集,重点是对数据的新增,所以对表加锁就可以解决

不可重复读和幻读的区别
从总的来看,两者都是对数据进行了两次查询,但两次查询的结果都不一样。
但如果你从控制的角度来看, 两者的区别就比较大
对于前者, 只需要锁住满足条件的记录
对于后者, 要锁住满足条件及其相近的记录

避免不可重复读需要锁行。
避免幻影读则需要锁表。

确实这两者有些相似。但不可重复读重点在于update和delete,而幻读的重点在于insert。

@Transcation失效的场景

1.如果Transactional注解应用在非public 修饰的方法上,Transactional将会失效。

之所以会失效是因为在Spring AOP 代理时,如上图所示 TransactionInterceptor (事务拦截器)在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource的 computeTransactionAttribute 方法,获取Transactional 注解的事务配置信息。

protected TransactionAttribute computeTransactionAttribute(Method method,
    Class<?> targetClass) {
        // Don't allow no-public methods as required.
        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
        return null;
}

复制代码

此方法会检查目标方法的修饰符是否为 public,不是 public则不会获取@Transactional 的属性配置信息。

springAop动态代理 时 事务拦截器在目标方法执行前后进行拦截 回调用computerTransactionAttribute方法获取 @Transactinal注解配置的信息

此方法会检查目标方法的修饰符是否为 public,不是 public则不会获取@Transactional 的属性配置信息

2、@Transactional 注解属性 propagation 设置错误

PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。 TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

3、@Transactional 注解属性 rollbackFor 设置错误

rollbackFor可以指定触发回滚的异常类型 spring默认抛出了 未检出异常(继承自RuntimeException) 或者 Error 才回滚事务其他异常不会触发回滚事务

4、同一个类中方法调用,导致@Transactional失效

开发中避免不了会对同一个类里面的方法调用,比如有一个类Test,它的一个方法A,A再调用本类的方法B(不论方法B是用public还是private修饰),但方法A没有声明注解事务,而B方法有。则外部调用方法A之后,方法B的事务是不会起作用的。这也是经常犯错误的一个地方。

那为啥会出现这种情况?其实这还是由于使用Spring AOP代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。

5、异常被你的 catch“吃了”导致@Transactional失效

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值