select user0_.userId as userId0_, user0_.name as name0_, user0_.age as age0_
from USERINFO user0_ for update
除了Query对象外,也可以在使用Session的load()或是lock()时指定锁模式。
除了前面所提及的两种锁模式外,还有三种Hibernate内部自动对数据进行加锁的模式,但它的处理是与数据库无关的。
LockMode.WRITE:在insert或update时进行锁定,Hibernate会在调用save()方法时自动获得锁。
LockMode.READ:在读取记录时Hibernate会自动获得锁。
LockMode.NONE:没有锁。
如果数据库不支持所指定的锁模式,Hibernate会选择一个合适的锁替换,而不是抛出一个异常。
乐观锁
乐观锁(Optimistic Locking)认为资料的存取很少发生同时存取的问题,因而不做数据库层次上的锁定。为了维护正确的数据,乐观锁是使用应用程序上的逻辑来实现版本控制的。
在使用乐观锁策略的情况下,数据不一致的情况一旦发生,有几个解决方法,一种是先更新为主,一种是后更新为主,比较复杂的就是检查发生变动的数据来实现,或是检查所有属性来实现乐观锁。
Hibernate中通过检查版本号来判断数据是否已经被其他人所改动,这也是Hibernate所推荐的方式。在数据库中加入一个version字段记录,在读取数据时连同版本号一同读取,并在更新数据时比较版本号与数据库中的版本号,如果等于数据库中的版本号则予以更新,并递增版本号,如果小于数据库中的版本号就抛出异常。
下面就来在前面例子的基础上进行Hibernate乐观锁的测试。
首先需要修改前面所实现的业务对象,在其中增加一个version属性,用来记录该对象所包含数据的版本信息,修改后的User对象如清单14.5所示。
清单14.5 修改后的User对象
package cn.hxex.hibernate.lock;
public class User {
private String id;
private Integer version; // 增加版本属性
private String name;
private Integer age;
// 省略了getter和setter方法
……
}
然后是修改映射文件,增加version属性的配置。在这里需要注意的是,这里的version属性应该使用专门的
修改后的映射文件如清单14.6所示。
清单14.6 修改后的映射文件
< xml version="1.0" >
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
接下来还要进行测试主程序的修改。由于需要模拟两个人同时修改同一个记录的情况,所以在这里需要将主程序修改为是可以多线程执行的,然后在run()方法中,调用对User对象的修改程序。
实现后的主测试程序如清单14.7所示。
清单14.7 修改后的测试主程序
package cn.hxex.hibernate.lock;
import java.net.URL;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
public class LockMain extends Thread{
……
public void testOptimisticLock() {
SessionFactory sf = LockMain.getSessionFactory();
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
User userV1 = (User)session.load( User.class, "1" );
// 等第二个进程执行
try {
sleep( 3000 );
} catch (InterruptedException e) {
e.printStackTrace();
}
userV1.setAge(new Integer(32));
tx.commit();
session.close();
}
public void run() {
testOptimisticLock();
}
public static void main(String[] args) {
// LockMain main = new LockMain();
// main.testPessimisticLock();
LockMain main1 = new LockMain();
main1.start();
LockMain main2 = new