业务逻辑的实现过程中,往往需要保证数据访问的排他性。因此,我们就需要通过一些机制来保证这些数据在某个操作过程中不会被外界修改,这样的机制,在这里,也就是所谓的“锁”,即给我们选定的目标数据上锁,使其无法被其它程序修改。
hibernate 支持两种锁机制:
1. 悲观锁(Pessimistic Locking);
2. 乐观锁(Optimistic Locking);
★ 悲观锁
它指的是对数据被外界修改保持保守态度,因些,在整个数据处理过程中,将数据牌锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层的锁机制才能保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
一个典型的悲观锁调用示例:
select * from account where name = "12345" for update
通过for update子句,这条SQL锁定了account表中所有符合检索条件的记录。本次事务提交之前(事务提交时会释放事务过程中的锁),外界无法修改这些记录。
Hibernate 的悲观锁,也是基于数据库的锁机制实现。
下面的代码实现了对查询记录的加锁:
String hqlStr = "from TUser as user where user.name='123456'";
Query query = session.createQuery(hqlStr);
query.setLockMode("user",LockMode.UPDATE);//加锁
List userList = query.list();
Hibernate 的加锁模式:
◆ LockMode.NONE:无锁机制
◆ LockMode.WRITE:Hibernate 在Insert 和Update记录的时候会自动获取
◆ LockMode.READ:Hibernate在读取记录的时候会自动获取
以上三种锁机制一般由Hibernate内部使用,如Hibernate为了保证Update过程中对象不会被外界修改,会在save方法实现中自动为目标添加上WRITE锁。这些都是Hibernate内部对数据的锁定机制,与数据库无关。
◆ LockMode.UPGRADE:利用数据库的for update子句
◆ LockMode.UPGRADE_NOWAIT:Oracle的特定实现,利用Oracle的for update nowait子句实现加锁
以上二种锁机制是我们在应用层较为常用的,依赖数据库的悲观锁机制。
★ 乐观锁
相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制,以操作最大程度的独占性。但随着而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。
乐观锁,大多是基于数据版本(Version)记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于库表的版本解决方案中,一般是通过为数据库表增加version字段来实现。
读取出数据时,将此版本号一同读出,之生更新时,对此版本号加1.此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。
例如,两个人同时在同一个帐号取钱,账号有100,A取50,B取20,A先提交,B的余额即为80,这时就不同步了,A提交后版本已经变了2了,而B看到的还是1的版本,此时B的提交必须被驳回。
需要注意的是,乐观锁机制往往基于系统中的数据存储逻辑,因此也具备一定的局限性。如有些例子,由于乐观锁机制是在我们的系统中实现,来自外部系统的用户余额更新操作不受我们系统控制,因此可能会造成非法数据被更新到数据库中。
在系统设计阶段,我们应该充分考虑到某些情况出现的可能性,并进行相应调整(如将乐观锁策略在数据库存储过程中实现,对外只开放基于此存储过程的数据更新途径,而不是将数据库表直接对外公开)。
Hibernate 在其数据访问引擎中内置了乐观锁实现。如果不用考虑外部系统对数据库的更新操作,利用Hibernate提供的透明化乐观锁实现,将大大提升我们的生产力。
Hibernate 中可以通过class描述符的optimistic-lock属性结合version描述符指定。
1.如:
<hibernate-mapping>
<class
name = "org.hibernate.sample.TUser"
table="t_user"
dynamic-update="true"
dynamic-insert="true"
optimistic-lock="version">
......
</class>
</hibernate-mapping>
optimistic-lock属性有如下几个值可选:
◆ none:无乐观锁;
◆ version:通过版本机制实现乐观锁;
◆ dirty:通过检查发生变动过的属性实现乐观锁;
◆ all:通过检查所有属性实现乐观锁。
其中通过version实现的乐观锁机制是Hibernate官方推荐的乐观锁实现,同时也是Hibernate中,目前惟一在实体对象脱离Session发生修改的情况下依然有效的锁机制。因此,一般情况下,我们都选择version方式作为Hibernate乐观锁实现机制。
2.添加一个Version属性描述符
<hibernate-mapping>
<class
name = "org.hibernate.sample.TUser"
table="t_user"
dynamic-update="true"
dynamic-insert="true"
optimistic-lock="version">
<id
name="id"
column="id"
type = "Java.lang.Integer">
<generator class="native"></generator>
</id>
<version
column="version"
name="version"
type="java.lang.Integer"/>
......
</class>
</hibernate-mapping>
注意,version节点必须出现在ID节点之后。