Hibernate3学习笔记(11)——Hibernate事务处理

来源:互联网 发布:php setlocale 中文 编辑:程序博客网 时间:2024/05/16 11:43

一、事务
1.1、事务的基本概念
事务是并发控制的基本单位。
事务的4个基本特征:
原子性,一致性,隔离性,持久性。
1.2、事务可能出现的几种不确定情况:
(1)、更新丢失(lost update):两个事务都同时更新一行数据,但是第二个事务却中途失败退出;导致对数据的两个修改都失效了。这是因为系统没有执行任何的锁操作,因此并发事务并没有被隔离开来;
(2)、脏读取(dirty reads) : 一个事务开始读取了某行数据,但是另外一个事务已经更新了此数据但没有能够及时提交。这相当危险,因为很可能所有的操作都被回滚;
(3)、不可重复读取(Non-repeatable Reads):一个事务对同一行数据重复读取两次,但是却得到了不同的结果。例如,在两次读取的中途,有另外一个事务对该行数据进行了修改,并提交。
(4)、两次更新问题(Second lost updates problem):无法重复读取的特例。有两个并发事务同时读取同一行数据,然后其中一个对它进行修改提交,而另一个也进行修改提交。这就会造成第一次写操作失效。
(5)、虚读(Phantom Reads): 事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据(这里并不要求两次查询的SQL语句相同)。这是因为在两次查询过程中有另外一个事务插入数据造成的。

二、数据库的隔离级别
为了避免上面出现的几种情况,在标准的SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同。

未授权读取(read uncommitted): 允许脏读取,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个数据则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现;
授权读取(read committed) :允许不可重复读取,但不允许脏读取。这可以通过“瞬间共享读锁”和“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行;
可重复读取(repeatable read) :禁止不可重复读取和脏读取,但是有时可能出现幻影数据。这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务;
序列化(serializable) :提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。

隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为read commited。他能够避免脏读取,而且具有较好的并发性能。尽管他会导致不可重复读,虚读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁和乐观锁来控制。

在选取隔离级别时,应该注意以下几个处理的原则:
1、首先,必须排除“未授权读取”,因为在多个事务之间使用它将会是非常危险的。事务的回滚操作或失败将会影响到其他并发事务。第一个事务的回滚将会完全将其他事务的操作清除;甚至使数据库处在一个不一致的状态。很可能一个已回滚为结束的事务对数据的修改最后却修改提交了,因为“未授权读取”允许其他事务读取数据,最后整个错误状态在其他事务之间传播开来。
2、其次,绝大部分应用都无须使用“序列化”隔离(一般来说,读取幻影数据并不是一个问题),此隔离级别也难以测量。目前使用序列化隔离的应用中,一般都使用悲观锁,这样强行使所有事务都序列化执行。
3、剩下的也就是在“授权读取”和“可重复读取”之间选择了。我们先考虑“可重复读取”。如果所有的数据访问都是在统一的原子数据库事务中,此隔离级别将消除一个事务在另外一个并发事务过程中覆盖数据的可能性(第二个事务更新丢失问题)。这是一个非常重要的问题。但是使用“可重复读取”并不是解决问题的唯一途径。
假设使用了“版本数据”,hibernate会自动使用版本数据。Hibernate的一级Session缓存和版本数据已经为你提供了“可重复读取隔离”绝大部分的特性。特别是,版本数据可以防止二次更新丢失问题,一级Session缓存可以保证持久载入数据的状态与其他事务对数据的修改隔离开来,因此如果使用对所有的数据库事务采用授权读取隔离和版本数据是行得通的。
“可重复读取”为数据库查询提供了更好的效率(仅对那些长时间的数据库事务),但是由于幻影读取依然存在,因此没必要使用它(对于WEB应用来说,一般也很少在一个数据库事务中对同一个表查询两次)。

综上所述,一般使用“授权读取”加其他措施即可了。

三、Hibernate中的事务处理
对于使用hibernate实现持久化功能的系统来说,事务的处理是这样的:服务器端在接收到用户的请求后,会创建一个新的hibernate session对象,然后通过该session对象开始一个新的事务并且之后所有对数据库的操作都通过该session对象来进行。最后,完成将响应页面发送到客户端的工作后再提交事务并且关闭Session。

注意:session对象是轻型的,非线程安全的,所以在每次用户请求时创建,请求处理完毕后丢弃。

四、并发控制
当数据库系统采用Read Committed隔离级别时,会导致不可重复读取和两次更新丢失的并发问题,可以在应用程序中采用锁机制来避免这类问题的产生。
从应用程序的角度上看,锁可以分为乐观锁和悲观锁两大类。

4.1、悲观锁
在多个客户端可能读取同一笔数据或同时更新一笔数据的情况下,必须要有访问控制的手段,防止同一个数据被修改而造成混乱,最简单的手段就是对数据进行锁定。在自己进行数据读取或更新等动作时,锁定其他客户端不能对同一笔数据进行任何的动作。
悲观锁(Pessimistic Locking):悲观地认定每次资料存取时,其他的客户端也会存取同一笔数据,因此将会锁住该笔数据,直到自己操作完成后再解除锁。影响性能。
悲观锁通常透过系统或数据库本身的功能来实现,依赖系统或数据库本身提供的锁机制。

4.2、乐观锁
乐观锁认为资料的存取很少发生同时存取的问题,因而不做数据库层次上的锁定。为了维护正确的数据,乐观锁是使用应用程序上的逻辑来实现版本控制的。
在使用乐观锁的策略的情况下,数据不一致的情况一旦发生,有几个解决方法,一种是先更新为主。一种是后更新为主,比较复杂的就是检查发生变动的数据来实现,或是检查所有属性来实现乐观锁。
hibernate中通过检查版本号来判断数据是否已经被其他人所改动,这也就是hibernate所推荐的方式。在数据库中加入一个version字段记录,在读取数据时连同版本号一同读取,并在更新数据时比较版本号,如果等于数据库中的版本号则予以更新,并递增版本号,如果小于数据库中的版本号就抛出异常。

0 0
原创粉丝点击