JAVA并发-本地事务

1. 事务特点

  • 原子性:一个事务中所有对数据库的操作是一个不可分割的操作序列,要么全做要么全不做
  • 一致性:数据不会因为事务的执行而遭到破坏
  • 隔离性:一个事务的执行,不受其他事务的干扰,即并发执行的事务之间互不干扰
  • 持久性:一个事务一旦提交,它对数据库的改变就是永久的。

2. 事务实现机制

Spring为事务管理提供了丰富的功能支持。Spring 事务管理分为编程式声明式的两种方式。

编程式事务管理: 编程式事务管理使用 TransactionTemplate 或者直接使用底层的 PlatformTransactionManager。对于编程式事务管理,spring 推荐使用 TransactionTemplate

声明式事务管理: 建立在 AOP 之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

声明式事务管理不需要入侵代码,更快捷而且简单,推荐使用。声明式事务有两种方式:

      • 一种是在配置文件(xml)中做相关的事务规则声明(因为很少用本文不讲解)
      • 另一种是基于 @Transactional 注解的方式。注释配置是目前流行的使用方式,推荐使用。

Spring AOP 代理有 CglibAopProxyJdkDynamicAopProxy 两种,以 CglibAopProxy 为例,对于 CglibAopProxy,需要调用其内部类的 DynamicAdvisedInterceptor 的 intercept 方法。对于 JdkDynamicAopProxy,需要调用其 invoke 方法。

3. 注解 @Transactional 的使用

3.1. 注解 @Transactional 常用配置

参 数 名 称

功 能 描 述

readOnly

用于设置当前事务是否为只读事务,设置为 true 表示只读,false 则表示可读写,默认值为 false。例如:@Transactional(readOnly=true)

rollbackFor

用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如:指定单一异常类:@Transactional(rollbackFor=RuntimeException.class);指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})

transactionManager / value

多个事务管理器托管在 Spring 容器中时,指定事务管理器的 bean 名称

rollbackForClassName

用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。例如:指定单一异常类名称 @Transactional(rollbackForClassName=”RuntimeException”) 指定多个异常类名称:@Transactional(rollbackForClassName={“RuntimeException”,”Exception”})

noRollbackFor

用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。例如:指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class) 指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class})

noRollbackForClassName

用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。例如:指定单一异常类名称:@Transactional(noRollbackForClassName=”RuntimeException”) 指定多个异常类名称:@Transactional(noRollbackForClassName={“RuntimeException”, ”Exception”})

propagation

用于设置事务的传播行为。例如:@Transactional(propagation=Propagation.NOT_SUPPORTED, readOnly=true)

isolation

用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置

timeout

该属性用于设置事务的超时秒数,默认值为 - 1 表示永不超时

3.2. Propagation 的属性(事务的传播行为)

@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)

Propagation 属性

含义

REQUIRED

默认值 在有 transaction 状态下执行;如当前没有 transaction,则创建新的 transaction;

SUPPORTS

如当前有 transaction,则在 transaction 状态下执行;如果当前没有 transaction,在无 transaction 状态下执行;

MANDATORY

必须在有 transaction 状态下执行,如果当前没有 transaction,则抛出异常 IllegalTransactionStateException;

REQUIRES_NEW

创建新的 transaction 并执行;如果当前已有 transaction,则将当前 transaction 挂起;

NOT_SUPPORTED

在无 transaction 状态下执行;如果当前已有 transaction,则将当前 transaction 挂起;

NEVER

在无 transaction 状态下执行;如果当前已有 transaction,则抛出异常 IllegalTransactionStateException。

3.3. 事务 5 种隔离级别

例如:@Transactional(isolation = Isolation.READ_COMMITTED)

隔离级别

含义

DEFAULT

这是一个 PlatfromTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别另外四个与 JDBC 的隔离级别相对应;

READ_UNCOMMITTED

最低的隔离级别。事实上我们不应该称其为隔离级别,因为在事务完成前,其他事务可以看到该事务所修改的数据。而在其他事务提交前,该事务也可以看到其他事务所做的修改。可能导致脏,幻,不可重复读

READ_COMMITTED

大多数数据库的默认级别。在事务完成前,其他事务无法看到该事务所修改的数据。遗憾的是,在该事务提交后,你就可以查看其他事务插入或更新的数据。这意味着在事务的不同点上,如果其他事务修改了数据,你就会看到不同的数据。可防止脏读,但幻读和不可重复读仍可以发生。

REPEATABLE_READ

比 ISOLATION_READ_COMMITTED 更严格,该隔离级别确保如果在事务中查询了某个数据集,你至少还能再次查询到相同的数据集,即使其他事务修改了所查询的数据。然而如果其他事务插入了新数据,你就可以查询到该新插入的数据。可防止脏读,不可重复读,但幻读仍可能发生。

SERIALIZABLE

完全服从 ACID 的隔离级别,确保不发生脏读、不可重复读和幻影读。这在所有隔离级别中也是最慢的,因为它通常是通过完全锁定当前事务所涉及的数据表来完成的。代价最大、可靠性最高的隔离级别,所有的事务都是按顺序一个接一个地执行。避免所有不安全读取。

3.4. 防止事务失效

在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。

@Transactional 注解应该只被应用在 public 修饰的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 该注解,它也不会报错(IDEA 会有提示), 但事务并没有生效。

被外部调用的公共方法 A 有两个进行了数据操作的子方法 B 和子方法 C 的事务注解说明:

被外部调用的公共方法 A 未声明事务 @Transactional,子方法 B 和 C 若是其他类的方法且各自声明事务,则事务由子方法 B 和 C 各自控制

被外部调用的公共方法 A 未声明事务 @Transactional,子方法 B 和 C 若是本类的方法,则无论子方法 B 和 C 是否声明事务,事务均不会生效

被外部调用的公共方法 A 声明事务 @Transactional,无论子方法 B 和 C 是不是本类的方法,无论子方法 B 和 C 是否声明事务,事务均由公共方法 A 控制

被外部调用的公共方法 A 声明事务 @Transactional,子方法运行异常,但运行异常被子方法自己 try-catch 处理了,则事务回滚是不会生效的!

如果想要事务回滚生效,需要将子方法的事务控制交给调用的方法来处理:

方案 1:子方法中不用 try-catch 处理运行异常

方案 2:子方法的 catch 里面将运行异常抛出【throw new RuntimeException();】

默认情况下,Spring 会对 unchecked 异常进行事务回滚,也就是默认对 RuntimeException() 异常或是其子类进行事务回滚。

如果是 checked 异常则不回滚,例如空指针异常、算数异常等会被回滚;文件读写、网络问题 Spring 就没法回滚。

若想对所有异常(包括自定义异常)都起作用,注解上面需配置异常类型:@Transactional(rollbackFor = Exception.class

数据库要支持事务,如果是 mysql,要使用 innodb 引擎,myisam 不支持事务

事务 @Transactional 由 spring 控制时,它会在抛出异常的时候进行回滚。如果自己使用 try-catch 捕获处理了,是不生效的。如果想事务生效可以进行手动回滚或者在 catch 里面将异常抛出【throw new RuntimeException();】

方案一:手动抛出运行时异常(缺陷是不能在 catch 代码块自定义返回值)

try{
      ....  
  }catch(Exception e){
      logger.error("",e);
      throw new RuntimeException(e);
  }

方案二:手动进行回滚【TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 】

try{
      ...
  }catch(Exception e){
      log.error("fail",e);
      TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
      return false;
  }

@Transactional 可以放在 Controller 下面直接起作用,看到网上好多同学说要放到 @Component 下面或者 @Service 下面,经过试验,可以不用放在这两个下面也起作用。

@Transactional 引入包问题,它有两个包:

import javax.transaction.Transactional; 
// 和
import org.springframework.transaction.annotation.Transactional;         // 推荐

4. 使用场景

参考:Spring Boot 各种事务操作实战(自动回滚、手动回滚、部分回滚)_手动回滚事务-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值