1. 含义
一组要么同时成功提交,要么同时失败回滚的sql语句,是数据库操作的一个执行单元。
2. 特性-ACID
1)原子性(Atomicity)
强调事务中的多个操作是一个整体:事务内所有操作是一个整体、最小执行单元,要么全部成功,要么全部失败。
2)一致性(Consistency)
强调事务中不会保存不一致的状态:从一个一致性状态转到另一个一致性状态,有一个失败,全部回滚。
3)隔离性(Isolation)
强调数据库事务中事务之间互相不可见:一个事务所做的修改在提交之前,其余的事务是不可见的,看不到事务的中间状态。
4)持久性(Durability)
强调数据库能永久保存数据库,一旦提交就不可撤销:事务提交,所有的修改都会永久保存到数据库。
3. 事务的并发问题
1)脏读
事务A读取了事务B尚未提交的数据。如果此时事务B由于失败导致数据回滚,那么事务A读取的数据就是脏数据。
2)不可重复读(前后多次读取,数据内容不一致)
事务A读取了事务B提交的数据。事务A查询时,事务B查询并修改了同一条数据并提交,事务A再次读取到事务B修改后的数据,发现两次读取数据不一致,系统读取不到重复的数据,成为不可重复读。
3)幻读(前后多次读取,数据总量不一致)
事务A读取了事务B新增或删除的数据,导致总数量不一致。事务A查询时,事务B新增了数据并提交,事务A再次查询时发现数据总量多了几条,就像产生了幻觉。
4)小总结
脏读:读取了另一事务未提交的数据。
不可重复读:读取了另一事务提交了的更改数据,针对update。
幻读:读取了另一事务提交了的新增或删除数据,针对insert或delete。
4. 事务的隔离级别
数据库事务的隔离级别有4个,由低到高分别是Read uncommitted 、Read committed 、Repeatable read 、Serializable,级别越高,数据越安全,但性能越低。
分别可以解决脏读、不可重复读、幻读的问题。
注意:Mysql中默认的隔离级别是:Repeatable read(可重复读)
5. 事务的传播行为
REQUIRED :如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
SUPPORTS :如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
MANDATORY :如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
REQUIRES_NEW :创建一个新的事务,如果当前存在事务,则把当前事务挂起。
NOT_SUPPORTED :以非事务方式运行,如果当前存在事务,则把当前事务挂起。
NEVER :以非事务方式运行,如果当前存在事务,则抛出异常。
NESTED :如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 REQUIRED。
6. 事务的实现
@Transactional注解,既可以添加在方法上又可以添加在类上,当注解添加在类级别上,表示该类所有的公共方法都配置了相同的事务属性信息,
如果类级别和方法级别都配置了@Transactional,应用程序会优先以方法级别的事务属性来管理事务,换言之,方法级别的事务属性会覆盖类级别的事务属性。
建议:在业务层抛出异常,控制层统一处理。
7. 事务不生效
1)访问权限问题
java的访问权限主要有四种:private、default、protected、public,从左往右,权限依次变大;spring要求被代理的方法必须是public,其余权限spring不提供事务。
2)方法用final、static修饰
spring事务底层使用aop实现,jdk或cglib动态代理,如果使用final或static修饰了方法,在代理类里无法重写,无法实现事务。
3)方法内部调用
同一个类中有事务A的方法A调用了有事务B的方法B,相当于直接使用this调用方法B,所以事务B不生效
解决方法
a. 新加一个service
b. 在该service注入自己
c. 通过AopContent类
4)未被spring管理
使用spring的前提是,对象要被spring管理,如果service上不加@service注解,该类不由spring管理,事务自然不生效。
5)表不支持事务
6)多线程调用
7)未开启事务
8. 事务不回滚
1)错误的传播行为
@Transactional(propagation = Propagation.NEVER),这种传播行为不支持事务,有事务会抛异常
2)自己吞了异常-try...catch
主动捕捉了异常并处理,导致框架无法感知异常,无法回滚。
3)手动抛了别的异常
默认RuntimeException(运行时异常)和error(错误)回滚,Eexception(非运行时异常)就不回滚。
4)嵌套事务回滚(多了)
实现只回归内部,不回滚外部,在内部手动捕捉异常-try...catch,并不继续往外抛。