樂(lè)觀并發(fā)控制,可以有三種方式。
1,Version版本號(hào)
2,時(shí)間戳
3,自動(dòng)版本控制。
這里不建議在新的應(yīng)用程序中定義沒(méi)有版本或者時(shí)間戳列的版本控制:它更慢,更復(fù)雜,如果你正在使用脫管對(duì)象,它則不會(huì)生效。
以下信息來(lái)自 : http://esffor.javaeye.com/blog/168243
通過(guò)在表中及POJO中增加一個(gè)version字段來(lái)表示記錄的版本,來(lái)達(dá)到多用戶同時(shí)更改一條數(shù)據(jù)的沖突
數(shù)據(jù)庫(kù)腳本:
create table studentVersion (id varchar(32),name varchar(32),ver int);
POJO
package Version;


public class Student {
private String id;
private String name;
private int version;

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getVersion() {
return version;
}

public void setVersion(int version) {
this.version = version;
}


}
Student.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Mapping file autogenerated by MyEclipse - Hibernate Tools
-->
<hibernate-mapping>
<class name="Version.Student" table="studentVersion" >
<id name="id" unsaved-value="null">
<generator class="uuid.hex"></generator>
</id>
<!--version標(biāo)簽必須跟在id標(biāo)簽后面-->
<version name="version" column="ver" type="int"></version>
<property name="name" type="string" column="name"></property>
</class>

</hibernate-mapping>
Hibernate.cfg.xml
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<!-- Generated by MyEclipse Hibernate Tools. -->
<hibernate-configuration>

<session-factory>
<property name="connection.username">root</property>
<property name="connection.url">
jdbc:mysql://localhost:3306/schoolproject?characterEncoding=gb2312&useUnicode=true
</property>
<property name="dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="myeclipse.connection.profile">mysql</property>
<property name="connection.password">1234</property>
<property name="connection.driver_class">
com.mysql.jdbc.Driver
</property>
<property name="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="hibernate.show_sql">true</property>
<property name="current_session_context_class">thread</property>
<property name="jdbc.batch_size">15</property>
<mapping resource="Version/Student.hbm.xml" />




</session-factory>

</hibernate-configuration>
測(cè)試代碼:
package Version;


import java.io.File;
import java.util.Iterator;
import java.util.Set;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;


public class Test {



public static void main(String[] args) {

String filePath=System.getProperty("user.dir")+File.separator+"src/Version"+File.separator+"hibernate.cfg.xml";
File file=new File(filePath);
System.out.println(filePath);
SessionFactory sessionFactory=new Configuration().configure(file).buildSessionFactory();
Session session=sessionFactory.openSession();
Transaction t=session.beginTransaction();
Student stu=new Student();
stu.setName("tom11");
session.save(stu);
t.commit();

/*
* 模擬多個(gè)session操作student數(shù)據(jù)表
*/
Session session1=sessionFactory.openSession();
Session session2=sessionFactory.openSession();
Student stu1=(Student)session1.createQuery("from Student s where s.name='tom11'").uniqueResult();
Student stu2=(Student)session2.createQuery("from Student s where s.name='tom11'").uniqueResult();
//這時(shí)候,兩個(gè)版本號(hào)是相同的
System.out.println("v1="+stu1.getVersion()+"--v2="+stu2.getVersion());
Transaction tx1=session1.beginTransaction();
stu1.setName("session1");
tx1.commit();
//這時(shí)候,兩個(gè)版本號(hào)是不同的,其中一個(gè)的版本號(hào)遞增了
System.out.println("v1="+stu1.getVersion()+"--v2="+stu2.getVersion());
Transaction tx2=session2.beginTransaction();
stu2.setName("session2");
tx2.commit();
}

}
運(yùn)行結(jié)果:
Hibernate: insert into studentVersion (ver, name, id) values (?, ?, ?)
Hibernate:
select student0_.id as id0_, student0_.ver as ver0_, student0_.name as
name0_ from studentVersion student0_ where student0_.name='tom11'
Hibernate:
select student0_.id as id0_, student0_.ver as ver0_, student0_.name as
name0_ from studentVersion student0_ where student0_.name='tom11'
v1=0--v2=0
Hibernate: update studentVersion set ver=?, name=? where id=? and ver=?
v1=1--v2=0
Hibernate: update studentVersion set ver=?, name=? where id=? and ver=?
Exception in thread "main" org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [Version.Student#4028818316cd6b460116cd6b50830001]
可以看到,第二個(gè)“用戶”session2修改數(shù)據(jù)時(shí)候,記錄的版本號(hào)已經(jīng)被session1更新過(guò)了,所以拋出了紅色的異常,我們可以在實(shí)際應(yīng)用
中處理這個(gè)異常,例如在處理中重新讀取數(shù)據(jù)庫(kù)中的數(shù)據(jù),同時(shí)將目前的數(shù)據(jù)與數(shù)據(jù)庫(kù)中的數(shù)據(jù)展示出來(lái),讓使用者有機(jī)會(huì)比較一下,或者設(shè)計(jì)程序自動(dòng)讀取新的數(shù)
據(jù)
注意:
要注意的是,由于樂(lè)觀鎖定是使用系統(tǒng)中的程式來(lái)控制,而不是使用資料庫(kù)中的鎖定機(jī)制,因而如果有人特意自行更新版本訊息來(lái)越過(guò)檢查,則鎖定機(jī)制就會(huì)無(wú)效,例如在上例中自行更改stu的version屬性,使之與資料庫(kù)中的版本號(hào)相同的話就不會(huì)有錯(cuò)誤,像這樣版本號(hào)被更改,或是由于資料是由外部系統(tǒng)而來(lái),因而版本資訊不受控制時(shí),鎖定機(jī)制將會(huì)有問(wèn)題,設(shè)計(jì)時(shí)必須注意。
如果手工設(shè)置stu.setVersion()自行更新版本以跳過(guò)檢查,則這種樂(lè)觀鎖就會(huì)失效,應(yīng)對(duì)方法可以將Student.java的setVersion設(shè)置成private