事务的特性
- 原子性(Atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。
- 一致性(Consistency):一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。举例来说,假设用户A和用户B两者的钱加起来一共是1000,那么不管A和B之间如何转账、转几次账,事务结束后两个用户的钱相加起来应该还得是1000,这就是事务的一致性。
- 隔离性(Isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。
- 持久性(Durability):持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
Spring事务中的隔离级别
在TransactionDefinition接口中定义了五个表示隔离级别的常量:
- ISOLATION_DEFAULT:使用后端数据库默认的隔离级别,Mysql默认采用的REPEATABLE_READ隔离级别;Oracle默认采用的READ_COMMITTED隔离级别。
- ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
- ISOLATION_READ_COMMITTED:允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
- ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
- ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
在TransactionDefinition接口中定义了八个表示事务传播行为的常量。
Spring事务中有哪几种事务传播行为?
在TransactionDefinition接口中定义了八个表示事务传播行为的常量
支持当前事务的情况:
- PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
- PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
- PROPAGATION_MANDATORY:
如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)。
不支持当前事务的情况:
- PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
- PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
- PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。
其他情况:
- PROPAGATION_NESTED:
如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于PROPAGATION_REQUIRED。
事务传播说明:
REQUIRED:
支持事务。如果业务方法执行时已经在一个事务中,则加入当前事务,否则重新开启一个事务。
外层事务提交了,内层才会提交。
内/外只要有报错,他俩会一起回滚。
只要内层方法报错抛出异常,即使外层有try-catch,该事务也会回滚!
因为内外层方法在同一个事务中,内层只要抛出了异常,这个事务就会被设置成rollback-only,即使外层try-catch内层的异常,该事务也会回滚。
REQUIRES_NEW:
支持事务。每次都是创建一个新事物,如果当前已经在事务中了,会挂起当前事务。
内层事务结束,内层就提交了,不用等着外层一起提交。
外层报错回滚,不影响内层。
内层报错回滚,外层try-catch内层的异常,外层不会回滚。
内层报错回滚,然后又会抛出异常,外层如果没有捕获处理内层抛出来的这个异常,外层还是会回滚的。这是重点!!!网上有些文章的例子给错了!!!
NESTED:
该传播行为解释:支持事务。如果当前已经在一个事务中了,则嵌套在已有的事务中作为一个子事务。如果当前没在事务中则开启一个事务。
内层事务结束,要等着外层一起提交。
外层回滚,内层也回滚。
如果只是内层回滚,不影响外层。
这个内层回滚不影响外层的特性是有前提的,否则内外都回滚。
使用前提:
1.JDK版本要在1.4以上,有java.sql.Savepoint。因为nested就是用savepoint来实现的。
2.事务管理器的nestedTransactionAllowed属性为true。
3.外层try-catch内层的异常
SUPPORTS:
该传播行为解释:支持事务。当前有事务就支持。当前没有事务就算了,不会开启一个事物
内层为SUPPORTS,外层为默认事务,内层等外层事务结束后,然后一起提交;
直接调用内层,按无事务管理;
MANDATORY:
该传播行为解释:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常
内层为MANDATORY ,外层没事务,则抛异常;
内层为MANDATORY ,外层有事务,内层等外层事务结束后,然后一起提交;
NOT_SUPPORTED:
以非事务方式运行,如果当前存在事务,则把当前事务挂起
内层使用NOT_SUPPORTED,外层使用默认事务,结果,内层不需等待外层事务结束,就已经提交了;
NEVER:
不支持事务。如果当前已经在一个事务中了,抛出异常
内存为NEVER,外层为默认,会抛异常;
脏读、不可重复读、幻读解释:
脏读:脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据
1. Mary的原工资为1000,财务人员将Mary的工资改为了8000,但未提交事务
2. 与此同时,Mary正在读取自己的工资.Mary发现自己的工资变为了8000,欢天喜地! (脏读)
3. 而财务发现操作有误,而回滚了事务,Mary的工资又变为了1000.
不可重复读:在一个事务中前后两次读取的结果并不致,导致了不可重复读
1. 在事务1中,Mary 读取了自己的工资为1000,操作并没有完成 .
2. 在事务2中,这时财务人员修改了Mary的工资为2000,并提交了事务.
3. 在事务1中,Mary 再次读取自己的工资时,工资变为了2000.
幻读:第一个事务对一个表中的全部数据行进行了修改。同时,第二个事务向表中插入一行新数据。那么操作第一个事务的用户发现表中还有未修改的数据行
1. 事务1,读取所有工资为1000的员工。
2. 这时事务2向employee表插入了一条员工记录,工资也为1000
3. 事务1再次读取所有工资为1000的员工 共读取到了11条记录