事务
事务的四大特性
- 原子性(Atomicity):事务最基本的操作单元,要么全部成功,要么全部失败,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。
- 一致性(Consistency):事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。如果事务成功地完成,那么系统中所有变化将正确地应用,系统处于有效状态。如果在事务中出现错误,那么系统中的所有变化将自动地回滚,系统返回到原始状态。
- 隔离性(Isolation):指的是在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。事务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据。
- 持久性(Durability):指的是只要事务成功结束,它对数据库所做的更新就必须永久保存下来。即使发生系统崩溃,重新启动数据库系统后,数据库还能恢复到事务成功结束时的状态。
事务传播级别:定义的是事务的控制范围
- PROPAGATION_REQUIRED:默认的spring事务传播级别,使用该级别的特点是,如果上下文中已经存在事务,那么就加入到事务中执行,如果当前上下文中不存在事务,则新建事务执行。
- PROPAGATION_SUPPORTS:如果上下文存在事务,则支持事务加入事务,如果没有事务,则以非事务的方式执行。这个通常是用来处理那些并非原子性的非核心业务逻辑操作。
- PROPAGATION_MANDATORY:该级别的事务要求上下文中必须要存在事务,否则就会抛出异常!配置该方式的传播级别是有效的控制上下文调用代码遗漏添加事务控制的保证手段。比如一段代码不能单独被调用执行,但是一旦被调用,就必须有事务包含的情况,就可以使用这个传播级别。
- PROPAGATION_REQUIRES_NEW:每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。
- PROPAGATION_NOT_SUPPORTED:不支持事务,如果上下文中存在事务,则挂起事务,执行当前逻辑,结束后恢复上下文的事务。
- PROPAGATION_NEVER:要求上下文中不能存在事务,一旦有事务,就抛出runtime异常,强制停止执行!
- PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作。
嵌套是子事务套在父事务中执行,子事务是父事务的一部分,在进入子事务之前,父事务建立一个回滚点,叫save point,然后执行子事务,这个子事务的执行也算是父事务的一部分,然后子事务执行结束,父事务继续执行。重点就在于那个save point。看几个问题就明了了:
如果子事务回滚,会发生什么?
父事务会回滚到进入子事务前建立的save point,然后尝试其他的事务或者其他的业务逻辑,父事务之前的操作不会受到影响,更不会自动回滚。
如果父事务回滚,会发生什么?
父事务回滚,子事务也会跟着回滚!为什么呢,因为父事务结束之前,子事务是不会提交的,我们说子事务是父事务的一部分,正是这个道理。
那么:事务的提交,是什么情况?
是父事务先提交,然后子事务提交,还是子事务先提交,父事务再提交?答案是第二种情况,还是那句话,子事务是父事务的一部分,由父事务统一提交。
数据库隔离级别:事务隔离级别定义的是事务在数据库读写方面的控制范围
- Read Uncommitted:是最低的事务隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。
- READ COMMITTED:大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统
- REPEATABLE READ(MySQL默认的隔离级别):保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。
- Serializable:最严格的级别,事务串行执行,资源消耗最大;
脏读:所谓的脏读,其实就是读到了别的事务回滚前的脏数据。比如事务B执行过程中修改了数据X,在未提交前,事务A读取了X,而事务B却回滚了,这样事务A就形成了脏读。
不可重复读:不可重复读字面含义已经很明了了,比如事务A首先读取了一条数据,然后执行逻辑的时候,事务B将这条数据改变了,然后事务A再次读取的时候,发现数据不匹配了,就是所谓的不可重复读了
幻读 :事务A首先根据条件索引得到10条数据,然后事务B改变了数据库一条数据,导致也符合事务A当时的搜索条件,这样事务A再次搜索发现有11条数据了,就产生了幻读。
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read Uncommitted | 会 | 会 | 会 |
Read Commited | 不会 | 会 | 会 |
Repeatable Read | 不会 | 不会 | 会 |
Serializable | 不会 | 不会 | 不会 |
Spring如何实现事务
核心类(Spring对事务的抽象)
- PlatformTransactionManager:事务管理器,包括事务的获取、提交和回滚
- TransactionProxyFactoryBean:
- TransactionInterceptor:事务拦截器
- TransactionStatus:当前事务状态
- TransactionDefinition:事务属性定义
事务几种实现方式
- 编程式事务管理对基于 POJO 的应用来说是唯一选择。我们需要在代码中调用beginTransaction()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。
- 基于 TransactionProxyFactoryBean 的声明式事务管理
- 基于 @Transactional 的声明式事务管理
- 基于Aspectj AOP配置事务
Spring AOP声明式事务实现原理
- 声明式事务的实现就是通过环绕增强的方式,在目标方法执行之前开启事务,在目标方法执行之后提交或者回滚事务
Spring对方法增强
- 前置增强:(org.springframework.aop.BeforeAdvice):在目标方法执行之前进行增强;
- 后置增强(org.springframework.aop.AfterReturningAdvice):在目标方法执行之后进行增强;
- 环绕增强(org.aopalliance.intercept.MethodInterceptor):在目标方法执行前后都执行增强;
- 异常抛出增强(org.springframework.aop.ThrowsAdvice):在目标方法抛出异常后执行增强;
- 引介增强(org.springframework.aop.IntroductionInterceptor):为目标类添加新的方法和属性。
各个平台实现
- JPA:JpaTransactionManager -> EntityManager
- Hibernate -> SessionFactory
- JDBC -> DataSource