Hibernate悲观锁和乐观锁
锁 ( locking )
?
业务逻辑的实现过程中,往往需要保证数据访问的排他性。如在金融系统的日终结算处理中,我们希望针对某个截止点的数据进行处理,而不希望在结算进行过程中(可能是几秒钟,也可能是几个小时),数据再发生变化。
?
此时,我们就需要通过一些机制来保证这些数据在某个操作过程中不会被外界修改,这样的机制,在这里,也就是所谓的“锁”,即给我们选定的目标数据上锁,使其无法被其他程序修改。
?
Hibernate 支持两种锁机制:即通常所说的“悲观锁( Pessimistic Locking )”和“乐观锁( OptimisticLocking )”。
?
?
悲观锁( Pessimistic Locking )
悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
?
一个典型的,依赖数据库实现的悲观锁调用:
?
?
通过 for update 子句,这条 SQL 锁定了 account 表中所有符合检索条件( name= “ Erica ”)的记录。本次事务提交之前(事务提交时会释放事务过程中的锁),外界无法修改这些记录。
?
Hibernate 的悲观锁,也是基于数据库的锁机制实现。
下面的代码实现了对查询记录的加锁:
?
optimistic-lock 属性有如下可选取值:
? none
无乐观锁。
? version
通过版本机制实现乐观锁。
? dirty
通过检查发生变动过的属性实现乐观锁。
? all
通过检查所有属性实现乐观锁。
?
其中通过 version 实现的乐观锁机制是 Hibernate 官方推荐的乐观锁实现,同时也是 Hibernate 中,目前惟一在实体对象脱离 Session 发生修改的情况下依然有效的锁机制。因此,一般情况下,我们都选择 version 方式作为 Hibernate 乐观锁实现机制。
?
2. 添加一个 Version 属性描述符
?
注意 ,version 节点必须出现在 ID 节点之后。
这里我们声明了一个 version 属性,用于存放用户的版本信息,保存在 T_User 表的 version 字段中。
此时如果我们尝试编写一段代码,更新 TUser 表中记录的数据,如:
Session session= getSession(); Criteria criteria = session.createCriteria(TUser.class); criteria.add(Expression.eq("name","Erica")); Session session2 = getSession(); Criteria criteria2 = session2.createCriteria(TUser.class); criteria2.add(Expression.eq("name","Erica")); List userList = criteria.list(); List userList2 = criteria2.list(); TUser user =(TUser)userList.get(0); TUser user2 =(TUser)userList2.get(0); Transaction tx = session.beginTransaction(); Transaction tx2 = session2.beginTransaction(); user2.setUserType(99); tx2.commit(); user.setUserType(1); tx.commit();?
执行以上代码,代码将在 tx.commit() 处抛出 StaleObjectStateException 异常,并指出版本检查失败,当前事务正在试图提交一个过期数据。通过捕捉这个异常,我们就可以在乐观锁校验失败时进行相应处理。