文章目录
1.事务
1.1 事务简介
- 事务管理是企业级应用程序开发中必不可少的技术, 用来确保数据的完整性和一致性.
- 事务就是一系列的动作, 它们被当做一个单独的工作单元. 这些动作要么全部完成, 要么全部不起作用
- 事务的四个关键属性( ACID)
原子性(atomicity)
: 事务是一个原子操作, 由一系列动作组成. 事务的原子性确保动作要么全部完成要么完全不起作用.
一致性(consistency)
: 一旦所有事务动作完成, 事务就被提交. 数据和资源就处于一种满足业务规则的一致性状态中.
隔离性(isolation)
: 可能有许多事务会同时处理相同的数据, 因此每个事物都应该与其他事务隔离开来, 防止数据损坏.
持久性(durability)
: 一旦事务完成, 无论发生什么系统错误, 它的结果都不
应该受到影响. 通常情况下, 事务的结果被写到持久化存储器中.
1.2 Spring事务管理
-
作为企业级应用程序框架, Spring 在不同的事务管理 API 之上定义了一个抽象层. 而应用程序开发人员不必了解底层的事务管理 API, 就可以使用 Spring 的事务管理机制.
-
Spring 既支持编程式事务管理, 也支持声明式的事务管理.
-
编程式事务管理: 将事务管理代码嵌入到业务方法中来控制事务的提交和回滚. 在编程式管理事务时, 必须在每个事务操作中包含额外的事务管理代码.
-
声明式事务管理: 大多数情况下比编程式事务管理更好用. 它 将事务管理代码从业务方法中分离出来, 以声明的方式来实现事务管理. 事务管理作为一种横切关注点, 可以通过 AOP 方法模块化. Spring 通过 Spring AOP
-
Spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。
-
Spring事务管理器的接口是org.springframework.transaction.PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了
1.3 场景
编写程序,实现购买一本书后,库存要减一,个人金额也要根据相应价格减少
1.3.1 注解版
1.没有加事务时
如上图,当没有加事务时,若第三步发生异常,那么虽然数据库里库存减少了,但是账户的余额还是没变,就导致业务数据不一致现场产生
2.添加事务管理器
如上图,当添加上事务管理器后,虽然第三步报错了,但是数据库里库存和账户金额都没有改变,因为事务并没有提交而是回滚到最初的位置
1.3.2 XML配置
1.4 事务传播行为
- 当事务方法被另一个事务方法调用时, 必须指定事务应该如何传播. 例如: 方法可能继续在现有事务中运行, 也可能开启一个新事务, 并在自己的事务中运行.
- 事务的传播行为可以由传播属性指定. Spring 定义了 7 种类传播行为.
1.4.1 场景
上面是只买了一本书,然后库存和个人金额相应介绍即可,现在是增加一个结账Cashier接口,一个人买多本书,后台循环支付处理
如上图,若AA用户,买1001书,买完后,账户没钱了,然后循环再买1002书,结果程序会报余额不足异常,那么整个流程执行下来,数据库的库存和金额并没有变化,因为checkout和purchase两个方法默认(REQUIRED)是在同一个事物里的,所以当其中有一个报异常,那么整个事物里之前的所有操作都会回滚的
1.4.2 REQUIRED(事物默认值)
1.4.3 REQUIRES_NEW
如上图所示,若在原来事物上设置REQUIRES_NEW,那么AA用户完第一本书后,数据库库存和金额就会立即变化,因为每次买书purchase,都会重新开启一个新的事物,原来事物挂起,一执行完,事物就会提交,所以买完第一本后,若金额不够,第二本再买,就报错,但是并不影响第一本的购买,最终结果就是第一本买了,数据库变化,第二本买不了,数据库不变
1.5 事物隔离级别
并发事务所导致的问题
- 当同一个应用程序或者不同应用程序中的多个事务在同一个数据集上并发执行时, 可能会出现许多意外的问题
- 并发事务所导致的问题可以分为下面三种类型:
脏读
: 对于两个事物 T1, T2, T1 读取了已经被 T2 更新但 还没有被提交的字段. 之后, 若 T2 回滚, T1读取的内容就是临时且无效的.
不可重复读
:对于两个事物 T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段. 之后, T1再次读取同一个字段, 值就不同了.
幻读
:对于两个事物 T1, T2, T1 从一个表中读取了一个字段, 然后 T2在该表中插入了一些新的行. 之后, 如果 T1 再次读取同一个表, 就会多出几行.
事务的隔离级别
- 从理论上来说, 事务应该彼此完全隔离, 以避免并发事务所导致的问题. 然而, 那样会对性能产生极大的影响, 因为事务必须按顺序运行.
- 在实际开发中, 为了提升性能, 事务会以较低的隔离级别运行.
- 事务的隔离级别可以通过隔离事务属性指定
设置隔离事务属性
如上图,可以根据这个级别,当AA用户买了第一本书后,因为在TX1事物里提交了,下次再买,从数据库里查出来的数据就已经是最新更新的了,查出来AA用户里之前买过书没有余额了,那么再买第二本书的时候,就会报异常
1.6 设置回滚属性
设置回滚事务属性
如上图,当对事物设置不回滚属性,比如对UserAccountException异常不进行回滚,当买第二本书的时候,第二步减少库存,第三步的时候,因为余额不足,按理应该直接回滚就完了,但是即使抛出了UserAccountException异常,但程序还是认为是正常结束的,不会进行回滚,所以最后买第二本书,数据库库存是减少的,但是账户金额还是不变的
1.7 超时和只读属性
如上图,若3秒时间到了事务还没执行完,就强制回滚