Hibernate学习三----事务
首先先回顾下事务的四大特性:
1、原子性:事务中的全部操作在数据库中是不可分割的,要么全部完成,要么均不执行。
2、一致性:几个并行执行的事务,其执行结果必须与按某一顺序串行执行的结果相一致。例子:一个人搬100块砖到A地点,是100块。五个搬100块到A还是100块。
3、隔离性:事务的执行不受其他事务的干扰,事务执行的中间结果对其他事务必须是透明的。
4、持久性:对于任意已提交事务,系统必须保证该事务对数据库的改变不被丢失,即使数据库出现故障。
事务的并发问题:
多个事务对数据库的并发操作,可能会破坏事务的隔离性与一致性。由于并发访问而引发的这样的问题称为事务的并发问题。可能引发的并发问题主要包括三类:脏读、不可重复读和丢失修改。
(1)脏读:A事务读取了B事务未提交的数据。
说明:对于事务隔离级别设置较低的数据库,用户对数据库的任何修改,都是会直接写入到数据库中的。但是,即使写入到数据库,若事务回滚,也是可以再将数据恢复为原数据。
所以可能发生脏读现象:A事务修改了某数据,虽未提交,但已写入到数据库。此时,B事务读取了改数据。然后A事务又发生了回滚。那么,B事务读取到的就是个“不存在”的脏数据。
(2)不可重复读有三种情况
A)事务A读取了某一数据后,事务B对其做了修改,当事务A再次读取该数据时,得到与前一次不同的值。:修改
B)事务A按一定条件从数据库中读取某数据记录后,事务B删除了其中部分记录,当A再次按相同条件读取数据时,发现某些记录神秘地消失了。:删除
C)事务A按一定条件从数据库中读取某数据记录后,事务B插入了一些记录,当A再次按相同条件读取数据时,发现多了一些记录。
后两种不可重复读,也称为幻影现象
(3)丢失修改
两个事务A与B,读入同一数据并修改,B提交的结果破坏了A提交的结果,导致A的修改丢失。
(4)幻读与幻影现象不是一回事儿
也称为“虚读”。这个问题是现实存在的由于对数据库的并发访问所引发的问题。
幻读是指,同一事务中,虽然多次执行相同的查询,查询结果是相同的。但后面的查询结果已经与DB中真正的数据不一致了。在同一事物中多次读取的数据,不是真实的DB中当前数据,是虚的数据,就想DB中数据的幻象。
例如:将数据放入缓存中,第一次从数据库查,后面从缓存中查。数据库修改了,但未与缓存同步。
由于设置了隔离级别,允许可重复读造成的。虚读可能更合适,因为读的是缓存的数据。
2、事务的隔离级别
隔离级别是为了事务的并发问题。
· 标准的SQL定义了四个隔离级别。级别由低到高分别为:读取未提交、读取已提交、可重复读、串行化。随着隔离级别的提高,其防止并发的效果也是逐步提高,但其系统开销也是逐步提高的,代码的执行效率逐步降低的。
封锁机制
事务的隔离级别,是DBMS隐式的为数据添加了锁。其底层实际上是在一个事务操作一个数据时,为该数据加了一把锁。只有该数据上所有的锁释放掉后,其他事务才可操作该数据。但这个操作仅指修改、删除,不包括查询,当然,出了串行化级别是表添加的表级锁外,其它隔离级别锁添加的锁均为行锁。
锁机制:分为乐观锁和悲观锁
(1)乐观锁:每次访问数据库都乐观的认为其他事务此时肯定不会同时修改该数据。在真正修改时,会在代码中通过对锁的状态判断来判断数据是否被其他事务修改过,是在代码中完成的,所以乐观锁是加在代码中的。
(2)悲观锁:每次访问数据时,都会悲观的认为其他事务一定会同时修改该数据。所以,其在访问数据时,在数据库中就会先给数据加锁。以防止其他事务同时修改该数据。所以锁是加在数据库中的。
乐观锁实现原理:一般充当乐观锁的数据有两类:版本号与时间戳。它们的工作原理是相同的。
会在从DB中读取数据时同时读出一个数据版本号。当A事务将修改过的数据写入到DB中时,会使版本号增加1.当B事务发生回滚或覆盖时,会首先对比自己数据的版本号与DB中数据的版本号。若相等,则说明DB中的数据没有发生变化,可修改或回滚到原始状态。若自己的版本号小于DB中的版本号,则说明其它事务已经修改过该数据,将跑出异常无法修改。
悲观锁:又分为两种:排他锁,X锁。写锁:共享锁,也称为S锁,读锁。
悲观锁是行锁,若要为表中的记录添加行锁,则需要通过执行查询语句来为符合条件的记录添加指定的锁。
在查询语句后添加for update,则会为每一条符合条件的记录添加写锁。例如:select * from student where id in(1,2,3) for update ;会为ID为1或2或3的记录添加写锁。
此时其他事务可以修改这三个ID以外的其他记录数据,可以查看所有记录数据,但不能修改id为1/2/3的记录数据,也不能再为这些数据添加其他类型的锁。
在查询语句后添加lock in share mode,则会为每一个符合条件的记录添加读锁。 select * from student where id in(1,2,3) lock in share mode。目的只有一个防止其他事务对这个数据加写锁,因为加上写锁后别人就可以修改了。我读取到的就不是最新的数据了。
3、Hibernate如何解决并发问题
Hibernate建议设置隔离级别为4级,即可重复读。
乐观锁如何实现在xml配置文件中添加vesion
<version name="sverion" column="tversion"></version>
实现悲观锁
Bean类的定义中不用增加任何属性。只需要在通过get()加载对象时,为其加锁。使用的get()方法原型为:
添加了写锁别人就无法修改。数据库中的该记录以及无法修改了。