Spring 并不直接管理事务,而是提供了多种事务管理器。在实际开发中,操作数据库时都会涉及到事务管理问题,为此Spring提供了专门用于事务处理的API。Spring的事务管理简化了传统的事务管理流程,并且在一定程度上减少了开发者的工作量。
一、事务的特性
事务应该具有4个属性:原子性、一致性、隔离性、持久性。
1、原子性:一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。
例如:A和B两个人一共1000元,A给B转账100元,A付款100元,B收款100元,
A的付款行为和B的收款行为要么都成功,要么都失败
2、一致性:事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
例如:A和B两个人一共1000元,无论A,B两人互相转账多少次,A和B两个人总额都应该是1000元
3、隔离性:一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
例如:万达影院有《阿凡达2》电影票100张,允许所有人同时去淘票票上购票,当第100张电影票被A,B,C3人同时购买,如果A拿到第100张电影票,但是还在犹豫要不要付钱,则B,C必须等待A的决定,如果A决定付钱,B.C就无法抢到票,如果A超时不付钱,则这第100张电影票回归票池,从新分配。
4、持久性:持久性也称永久性,指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
例如:万达影院有《阿凡达2》电影票100张,100张电影票销售完毕,对于每个购买者来说,他的购买记录已经产生,即使退票,他之前的购买记录也不会消失。
二、事务的基本事务属性
1、传播行为:当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。Spring定义了七种传播行为。
传播行为 | 含义 |
---|---|
REQUIRED(默认) | 当前如果有事务,Spring就会使用该事务;否则会开始一个新事务(增、删、改) |
SUPPORTS | 当前如果有事务,Spring就会使用该事务;否则不会开始一个新事务(查询) |
MANDATORY | 表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常 |
REQUIRED_NEW | 当前如果有事务,把当前事务挂起,新建事务 |
NOT_SUPPORTED | 当前有事务,Spring也会在非事务环境下执行。如果当前有事务,则该事务挂起 |
NEVER | 当前有事务,Spring也会在非事务环境下执行。如果当前有事务,则抛出异常 |
NESTED | 当前有事务,则在嵌套事务中执行。如果没有,那么执行情况与REQUIRED一样 |
2、隔离级别:定义了一个事务可能受其他并发事务的影响程度。多个事务并发运行,经常会操作相同的数据来完成各自的任务,可能会出现脏读,不可重复读和幻读的问题。
脏读:在一个事务中读取到了另外一个事务修改的【未提交的数据】,而导致多次读取同一个数据返回的结果不一致 (必须要解决的)
不可重复读:在一个事务中读取到了另外一个事务修改的【已提交的数据】,而导致多次读取同一个数据返回的结果不一致
幻读: 一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了。再后来的查询中,第一个事务就会发现有些原来没有的记录
隔离级别有四种
隔离级别 | 含义 |
---|---|
DEFAULT | spring默认数据库的隔离级别 |
READ_UNCOMMITTED | 最低的隔离级别,允读取尚未提交的数据交互,可能会导致脏读、欢读或不可重复读 |
READ_COMMITTED | 允许读取并发事务已经提交的数据,可以阻止幻读,但是幻读或不可重复读仍有可能发生 |
REPEATABLE_READ | 对同一字段的多次读取结果都是一致的,除非数据是被本身事务所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生 |
SERIALIZABLE | 最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复度以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现 |
隔离级别由低到高【读未提交】=>【读已提交】=>【可重复读】=>【序列化操作】
3、是否只读:如果一个方法内都是对数据库的select操作,那么可以设置方法事务为只读,数据库也会对该事务进行特定的优化。只读事务内不能有insert、update、delete的操作。
4、事务超时:事务可能设计对后端数据库的锁定,所以长时间的事务运行会不必要的占用数据库资源,设置事务超时时间可以及时释放资源
5、回滚规则:事务属性定义了哪些异常会导致事务回滚而哪些不会。默认情况下,事务只有遇到运行期异常时才会回滚,而在遇到检查型异常时不会回滚
三、声明式事务管理的实现
Spring事务管理分为两种方式:编程式事务管理、声明式事务管理
以下以银行转账业务示例
编程式事务管理是通过编写代码实现的事务管理。可以根据需求规定事务从哪里开始,到哪里结束,拥有很高的灵活性。但是这种方式,会使业务代码与事务规则高度耦合,难以维护,因此我们很少使用这种方式对事务进行管理。
声明式事务管理: 采用声明的方式来处理事务,可以通过 2 种方式实现,分别是 XML和注解方式。Spring 在进行声明式事务管理时,底层使用了 AOP 。事务管理不侵入开发的组件。
以下为声明式事务管理的实现
1、创建实体类Account,生成get、set方法
2、在dao包下创建AccountDao接口
3、在dao包的impl子包下创建目标类AccountDaoImpl
4、创建service接口和实现类
AccountService接口
AccountServiceImpl实现类
5、在Spring配置类上启动事务管理
6、配置类JdbcConfig
7、测试
测试结果
发生异常,涉及转账业务的双方的金额均不变