悲观锁假定任何时刻存取数据时,都有可能有另一个客户也正在存取同一笔资料,因而对资料采取了资料库层次的锁定状态,在锁定的时间内其他的客户不能对资料进行存取,对于单机或小系统而言,这并不成问题,然而如果是在网路上的系统,同时刻会有许多连线,如果每一次读取资料都造成锁定,那后面的存取就必须等待,这将造成效能上的问题,造成后面使用者的长时间等待。
乐观锁定则认为资料的存取很少发生同时存取的问题,因而不作资料库层次上的锁定,为了维护正确的资料,乐观锁定使用应用程式上的逻辑实现版本控制的解决。在不实行悲观锁定策略的情况下,资料不一致的情况一但发生,就会造成资料不一致。Hibernate的乐观锁处理是在数据中加入一个version栏位记录,在读取数据时,连同版本号一同读取,并在更新资料时对比版本号与数据库中的版本号,如果等于数据库中的版本号则予以更新,并递增版本号,如果小于资料库中的版本号就抛出异常。
新建一个表event。
新建Event.java
文件Event.hbm.xml
程序HibernateSessionFactory.java
程序BaseHibernateDAO.java
测试程序EventDao.java
执行EventDao.java
可以看到数据库中添加了数据,这是version的值是0。然后把程序中/***/的部分改一下
在执行,查询数据库,会发现version的数据递增成1了。
现在来测试一个例子,看看出现异常的情况。
首先要改程序HibernateSessionFactory.java里的getSession方法。
这时修改EventDao.java
运行,可以看到控制台上的输出。
Hibernate: select event0_.id as id0_0_, event0_.version as version0_0_, event0_.title as title0_0_ from event event0_ where event0_.id=?
Hibernate: select event0_.id as id0_0_, event0_.version as version0_0_, event0_.title as title0_0_ from event event0_ where event0_.id=?
v1 v2:12 12
Hibernate: update event set version=?, title=? where id=? and version=?
v1 v2:13 12
Hibernate: update event set version=?, title=? where id=? and version=?
严重: Could not synchronize database state with session
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.Event#1]
这时就是在event2更新时发生了版本异常。
要注意的是,乐观锁是在程序里做的控制,如果在更新数据时,人为的改变version的值,那么保存时,数据一样会被成功保存,这样锁定机制就有问题了,在设计时要注意。
乐观锁定则认为资料的存取很少发生同时存取的问题,因而不作资料库层次上的锁定,为了维护正确的资料,乐观锁定使用应用程式上的逻辑实现版本控制的解决。在不实行悲观锁定策略的情况下,资料不一致的情况一但发生,就会造成资料不一致。Hibernate的乐观锁处理是在数据中加入一个version栏位记录,在读取数据时,连同版本号一同读取,并在更新资料时对比版本号与数据库中的版本号,如果等于数据库中的版本号则予以更新,并递增版本号,如果小于资料库中的版本号就抛出异常。
新建一个表event。
create table event(id integer primary key,version integer,title varchar(20))
新建Event.java
package com;
public class Event {
private int id;
private String title;
private int version;
Event(){ }
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
}
文件Event.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">
<hibernate-mapping>
<class name="com.Event" table="Event">
<id name="id" column="id" length="32">
<generator class="increment" />
</id>
<version name="version"
column="version" type="java.lang.Integer" >
</version>
<property name="title" column="title"/>
</class>
</hibernate-mapping>
程序HibernateSessionFactory.java
package com;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.cfg.Configuration;
public class HibernateSessionFactory {
private static String CONFIG_FILE_LOCATION = "/hibernate.cfg.xml";
private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();
private static Configuration configuration = new Configuration();
private static org.hibernate.SessionFactory sessionFactory;
private static String configFile = CONFIG_FILE_LOCATION;
private HibernateSessionFactory() {
}
public static Session getSession() throws HibernateException {
Session session = (Session) threadLocal.get();
if (session == null || !session.isOpen()) {
if (sessionFactory == null) {
rebuildSessionFactory();
}
session = (sessionFactory != null) ? sessionFactory.openSession()
: null;
threadLocal.set(session);
}
return session;
}
public static void rebuildSessionFactory() {
try {
configuration.configure(configFile);
sessionFactory = configuration.buildSessionFactory();
} catch (Exception e) {
System.err
.println("%%%% Error Creating SessionFactory %%%%");
e.printStackTrace();
}
}
public static void closeSession() throws HibernateException {
Session session = (Session) threadLocal.get();
threadLocal.set(null);
if (session != null) {
session.close();
}
}
public static org.hibernate.SessionFactory getSessionFactory() {
return sessionFactory;
}
public static void setConfigFile(String configFile) {
HibernateSessionFactory.configFile = configFile;
sessionFactory = null;
}
public static Configuration getConfiguration() {
return configuration;
}
}
程序BaseHibernateDAO.java
package com;
import org.hibernate.Session;
public class BaseHibernateDAO {
public Session getSession() {
return HibernateSessionFactory.getSession();
}
public void closeSession(){
HibernateSessionFactory.closeSession();
}
}
测试程序EventDao.java
package com;
import java.util.Date;
import org.hibernate.Session;
import org.hibernate.Transaction;
import com.Event;
public class EventDao extends BaseHibernateDAO{
public static void main(String args[]) throws Exception{
new EventDao().add();
}
public void add() throws Exception {
Session sess = this.getSession();
Transaction tran = sess.beginTransaction();
try{
tran.begin();
/***/
Event event = new Event();
// Event event = (Event)sess.load(Event.class, 1);
event.setTitle("First super star");
/***/
sess.save(event);
tran.commit();
}catch(Exception e){
e.printStackTrace();
}finally{
this.closeSession();
}
}
执行EventDao.java
可以看到数据库中添加了数据,这是version的值是0。然后把程序中/***/的部分改一下
//Event event = new Event();
Event event = (Event)sess.load(Event.class, 1);
event.setTitle("Second super star");
在执行,查询数据库,会发现version的数据递增成1了。
现在来测试一个例子,看看出现异常的情况。
首先要改程序HibernateSessionFactory.java里的getSession方法。
public static Session getSession() throws HibernateException {
Session session = (Session) threadLocal.get();
if (sessionFactory == null) {
rebuildSessionFactory();
}
return sessionFactory.openSession();
}
这时修改EventDao.java
package com;
import org.hibernate.Session;
import org.hibernate.Transaction;
import com.Event;
public class EventDao extends BaseHibernateDAO{
public static void main(String args[]) throws Exception{
new EventDao().add();
}
public void add() throws Exception {
Session sess1 = this.getSession();
Session sess2 = this.getSession();
Transaction tran1 = sess1.beginTransaction();
Transaction tran2 = sess2.beginTransaction();
try{
Event event1 = (Event)sess1.load(Event.class, 1);
Event event2 = (Event)sess2.load(Event.class, 1);
System.out.println("v1 v2:"+event1.getVersion()+" "+event2.getVersion());
tran1.begin();
event1.setTitle("Modify one");
sess1.save(event1);
tran1.commit();
System.out.println("v1 v2:"+event1.getVersion()+" "+event2.getVersion());
tran2.begin();
event2.setTitle("Modify two");
sess2.save(event2);
tran2.commit();
sess1.close();
sess2.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
运行,可以看到控制台上的输出。
Hibernate: select event0_.id as id0_0_, event0_.version as version0_0_, event0_.title as title0_0_ from event event0_ where event0_.id=?
Hibernate: select event0_.id as id0_0_, event0_.version as version0_0_, event0_.title as title0_0_ from event event0_ where event0_.id=?
v1 v2:12 12
Hibernate: update event set version=?, title=? where id=? and version=?
v1 v2:13 12
Hibernate: update event set version=?, title=? where id=? and version=?
严重: Could not synchronize database state with session
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.Event#1]
这时就是在event2更新时发生了版本异常。
要注意的是,乐观锁是在程序里做的控制,如果在更新数据时,人为的改变version的值,那么保存时,数据一样会被成功保存,这样锁定机制就有问题了,在设计时要注意。