什么是事务
事务的概念
事务:是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作;这些操作作为一个整体一起向 系统提交,要么都执行、要么都不执行;事务是一组不可再分割的操作集合(工作逻辑单元);
通俗点说就是:一组sql语句组成的数据库逻辑处理单元,在这组的sql操作中,要么全部执行成功,要么全部执行失败。
最常见的例子就转账:
小明给如花转账:
开启事务 ------
1.从小明的账户扣除1000块
2.给如花的账户增加1000块
事务提交------
上面例子的任何步骤一旦出现问题,都会导致事务回滚。
从搭讪到结婚就是事务提交。 女方要求男方重新追求她一次就是事务回滚🤣。
事务的四大特性(ACID)
- 原子性 (Atomicity):事务中所有操作是不可再分割的原子单位。事务中所有操作要么全部执行成功,要么全部执行失败。
- 一致性(Consistency):一致性是指执行事务前后的状态要一致,可以理解为数据一致性。如转账业务,无论事务执行 成功与否,参与转账的两个账号余额之和应该是不变的。
- 隔离性(Isolation):隔离性是指在并发操作中,不同事务之间应该隔离开来,使每个并发中的事务不会相 互干扰。这个与事务设置的隔离级别有密切的关系。
- 持久性 (Durability): 一旦事务提交成功,事务中所有的数据操作都必须被持久化到数据库中。
原子性、隔离性、持久性都是为了保障一致性而存在的,一致性也是最终的目的。
事务的隔离级别
数据库事务的隔离级别有4种,由低到高分别为读未提交(READ UNCOMMITTED)、读提交 (READ COMMITTED)、可重复读 (REPEATABLE READ)、串行化(SERIALIZABLE)。 而且,在事务的并发操作中可能会出现脏读,不可重复读,幻读。
脏读:(读取了未提交的新事务,然后被回滚了)
事务A读取了事务B中尚未提交的数据。如果事务B回滚,则A读取使用了错误的数据。
不可重复读:(读取了提交的新事务,指更新操作)
不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。
幻读:(也是读取了提交的新事务,指增删操作)
在事务A多次读取中,事务B对数据进行了新增操作,导致事务A多次读取的数据不一致。
不可重复读,针对的是行数据被修改,避免的方法就是行锁,而幻读是针对增加或删除行数据,避免的方法是表锁,带来的开销代价不一样。
读未提交(READ UNCOMMITTED)
读未提交,顾名思义,就是一个事务可以读取另一个未提交事务的数据。 会产生脏读。
读提交 (READ COMMITTED)
读提交,顾名思义,就是一个事务要等另一个事务提交后才能读取数据。 会产生不可重复读。
可重复读 (REPEATABLE READ)
重复读,就是在开始读取数据(事务开启)时,不再允许修改操作。可能会产生幻读。
串行化(SERIALIZABLE)
Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但 是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
大多数数据库默认的事务隔离级别是Read committed, 比如Sql Server, Oracle。
Mysql的默认隔离级别是Repeatable read。
Spring事务配置
Spring支持编程式事务管理和声明式事务管理两种方式。
- 编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,Spring推荐使用TransactionTemplate。
- 声明式事务管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。显然声明式事务管理要优于编程式事务管理,这正是Spring倡导的非侵入式的开发方式。声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。和编程式事务相比,声明式事务唯一不足地方是,后者的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。
声明式事务管理也有两种常用的方式,一种是基于tx和aop名字空间的xml配置文件,另一种就是基于@Transactional注解。显然基于注解的方式更简单易用,更清爽。
@Override
@Transactional(rollbackFor = Exception.class)
public int delete(Long id) {
// do something
}
默认配置下 Spring 只会回滚运行时、未检查异常(继承自 RuntimeException 的异常)或者 Error。
如果想回滚所有异常 加上rollbackFor = Exception.class 即可。
@Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,@Transactional 注解将失效,也不会抛出任何异常。
同一个类中方法调用,导致@Transactional失效
接口中A、B两个方法,A无@Transactional标签,B有,上层通过A间接调用B,此时事务不生效。
其实这还是由于使用Spring AOP代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。
Spring事务的传播特性
什么是事务的传播特性?
指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。
代码举例
// ServiceA
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
@Transactional
public void methodA() {
System.out.println("methodA 执行了...");
serviceB.methodB();
}
}
// ServiceB
@Service
public class ServiceB {
@Transactional
public void methodB() {
System.out.println("methodB 执行了...");
}
}
Spring总共给出了7种事务传播行为:
关于事务挂起的举例:(某事务挂起之后,任何操作都不在该事务的控制之下)
例如: 方法A支持事务,方法B不支持事务。 即PROPAGATION_NOT_SUPPORTED
方法A调用方法B。
在方法A开始运行时,系统为它建立Transaction,方法A中对于数据库的处理操作,会在该Transaction的控制之下。
这时,方法A调用方法B,方法A打开的 Transaction将挂起,方法B中任何数据库操作,都不在该Transaction的管理之下。
当方法B返回,方法A继续运行,之前的Transaction恢复,后面的数据库操作继续在该Transaction的控制之下 提交或回滚。