(一)什么是事务:
事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。
特性:事务是恢复和并发控制的基本单位。
事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。任何事务机制在实现时,都应该考虑事务的ACID特性,包括:本地事务、分布式事务,即使不能都很好的满足,也要考虑支持到什么程度。
(二)ACID:
ACID 理论是对事务特性的抽象和总结,方便我们实现事务。你可以理解成:如果实现了操作的 ACID 特性,那么就实现了事务。ACID的具体含义详述如下。
原子性(Atomicity):原子性是指单个事务本身涉及到的数据库操作,要么全部成功,要么全部失败,不存在完成事务中一部分操作的可能。以上文说的转账为例,就是要么操作全部成功,A的钱少了,B的钱多了;要么就是全部失败,AB保持和原来一直的数目。
一致性(Consistency):事务必须是使数据库从一个一致性状态变到另一个一致性状态,事务的中间状态不能被观察到的。还是以转账为例,原来AB账户的钱加一起是1000,相互转账完成时候彼此还是1000,所以一致性理解起来就是事务执行前后的数据状态是稳定的,对于转账就是金额稳定不变,对于其他的事务操作就是事务执行完成之后,数据库的状态是正确的,没有脏数据。
隔离性(isolation):一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。隔离性侧重于多个事务之间的特性,也就是说多个事务之间是没有相互影响的比如A给B转账和B给C转账这两个事务是没有影响的(这里B给C转账如果和A给B转账的事务同时进行的时候,B的金额正确性问题保证就要看隔离级别了)。
持久性(durability):持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
(三)事务的隔离级别:
在多个事务并发操作数据库(多线程、网络并发等)的时候,如果没有有效的避免机制,就会出现脏读、不可重复读和幻读这3种问题。
脏读(Dirty Read):
A事务读取B事务尚未提交的数据,此时如果B事务由于某些原因执行了回滚(commit后是不能rollback的)操作,那么A事务读取到的数据就是脏数据(没有第二次读取,还是读取的B事务没有rollback前的数据)。
参考下图,事务A读取到了事务B未提交的记录。
不可重复读(Nonrepeatable Read):
一个事务内前后多次读取,数据内容不一致。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的(A第一次读取B未修改的数据,第二次读取时B已经修改)。这样在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。
参考下图,事务A读取到的name可能为“张三”,也可能为“李四”。
幻读(Phantom Read):
一个事务内前后多次读取,数据总量不一致。参考下图,事务A在执行读取操作,需要两次统计数据的总量,前一次查询数据总量后,此时事务B执行了新增数据的操作并提交后,这个时候事务A读取的数据总量和之前统计的不一样,就像产生了幻觉一样,平白无故的多了几条数据,成为幻读。
不可重复读和幻读有些相似,两者的区别在于:不可重复读的重点在于修改,同样的条件, 你读取过的数据,再次读取出来发现值不一样了;而幻读的重点在于新增或者删除(参考MySQL官网https://dev.mysql.com/doc/refman/5.7/en/innodb-next-key-locking.html对幻读的定义,记录的减少也应该算是幻读),同样的条件, 第 1 次和第 2 次读出来的记录数不一样。
隔离级别:
事务的隔离性是指多个并发的事务同时访问一个数据库时,一个事务不应该被另一个事务所干扰,每个并发的事务间要相互进行隔离。SQL 标准定义了以下四种隔离级别:
事务的隔离级别和脏读、不可重复读、幻读的关系总结如下表所示:
(四)事务的传播特性:
在项目开发中,一般将事务设置在service层,当调用这个service方法时,这个service保证了所有的操作在同一个事务中,所有对数据库的操作要么一起成功,要么一起失败。
如果service方法除了调用DAO,还调用了其他的service方法,如何通过事务控制,保证数据的一致,这个时候就需要事务的传播特性。
事务的传播特性共有7种:
PROPAGATION_REQUIRED:支持当前事务,如果不存在就新建一个(默认) ;
PROPAGATION_REQUIRES_NEW:开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起;
PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果有事务存在,挂起当前事务;
PROPAGATION_MANDATORY:必须运行在一个事务中,如果当前没有事务正在发生,将抛出一个异常;
PROPAGATION_NEVER:以非事务方式运行,如果有事务存在,抛出异常;
PROPAGATION_NESTED:如果当前正有一个事务在进行中,则该方法应当运行在一个嵌套式事务中。被嵌套的事务可以独立于封装事务进行提交或回滚。如果封装事务不存在,行为就像PROPAGATION_REQUIRES一样。
作用:
传播特性主要控制是使用老的事务,还是创建新的事务,或者事务是否运行在上下文中。
java使用举例:
@Transactional(propagation = Propagation.REQUIRED)
public void addStudent(){
。。。。
}
xml配置举例: