CREATE TABLE user ( id INT(11) NOT NULL auto_increment PRIMARY KEY, version INT, name VARCHAR(100) NOT NULL default '', age INT );这个user表格中的version用来记录版本号,以供Hibernate实现乐观锁定,接着设计User类别,当中必须包括version属性:
package onlyfun.caterpillar;在映射文件的定义方面,则如下所示:
public class User {
private Integer id;
private Integer version; // 增加版本屬性
private String name;
private Integer age;
public User() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
<?xml version="1.0" encoding="utf-8"?>注意<version>标签必须出现在<id>卷标之后,接着您可以试着在数据库中新增数据,例如:
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="onlyfun.caterpillar.User"
table="user"
optimistic-lock="version">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native"/>
</id>
<version name="version"
column="version"
type="java.lang.Integer"/>
<property name="name" column="name" type="java.lang.String"/>
<property name="age" column="age" type="java.lang.Integer"/>
</class>
</hibernate-mapping>
User user = new User(); user.setName( "caterpillar"); user.setAge(new Integer(30)); Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); session.save(user); tx.commit(); session.close();您可以检视数据库中的数据,每一次对同一笔数据进行更新,version字段的内容都会自动更新,接着来作个实验,直接以范例说明:
// 有使用1者开启了一个session1 Session session1 = sessionFactory.openSession(); // 在这之后,马上有另一个使用者2开启了session2 Session session2 = sessionFactory.openSession(); Integer id = new Integer(1); // 使用者1查询数据 User userV1 = (User) session1.load(User.class, id); // 使用者2查询同一笔数据 User userV2 = (User) session2.load(User.class, id); // 此时两个版本号是相同的 System.out.println( " v1 v2 "+ userV1.getVersion().intValue() + " " + userV2.getVersion().intValue()); Transaction tx1 = session1.beginTransaction(); Transaction tx2 = session2.beginTransaction(); // 使用者1更新数据 userV1.setAge(new Integer(31)); tx1.commit(); // 此时由于数据更新,数据库中的版本号递增了 // 两笔数据版本号不一样了 System.out.println( " v1 v2 "+ userV1.getVersion().intValue() + " " + userV2.getVersion().intValue()); // userV2 的 age 资料还是旧的 // 数据更新 userV2.setName( "justin"); // 因版本号比数据库中的旧 // 送出更新数据会失败,丢出StableObjectStateException例外 tx2.commit(); session1.close(); session2.close();运行以下的程序片段,会出现以下的结果:
Hibernate: select user0_.id as id0_, user0_.version as version0_0_, user0_.name as name0_0_, user0_.age as age0_0_ from user user0_ where user0_.id=? Hibernate: select user0_.id as id0_, user0_.version as version0_0_, user0_.name as name0_0_, user0_.age as age0_0_ from user user0_ where user0_.id=? v1 v2 0 0 Hibernate: update user set version=?, name=?, age=? where id=? and version=? v1 v2 1 0 Hibernate: update user set version=?, name=?, age=? where id=? and version=? 16:11:43,187 ERROR AbstractFlushingEventListener:277 - Could not synchronize database state with session org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [onlyfun.caterpillar.User#1] at org.hibernate.persister.entity.BasicEntityPersister.check(BasicEntityPersister.java:1441)由于新的版本号是1,而userV2的版本号还是0,因此更新失败丢出StableObjectStateException,您可以捕捉这个例外作善后 处理,例如在处理中重新读取数据库中的数据,同时将目前的数据与数据库中的数据秀出来,让使用者有机会比对不一致的数据,以决定要变更的部份,或者您可以 设计程序自动读取新的数据,并比对真正要更新的数据,这一切可以在背景执行,而不用让您的使用者知道。