乐观锁的理解

悲观锁假定任何时刻存取数据时,都有可能有另一个客户也正在存取同一笔资料,因而对资料采取了资料库层次的锁定状态,在锁定的时间内其他的客户不能对资料进行存取,对于单机或小系统而言,这并不成问题,然而如果是在网路上的系统,同时刻会有许多连线,如果每一次读取资料都造成锁定,那后面的存取就必须等待,这将造成效能上的问题,造成后面使用者的长时间等待。

乐观锁定则认为资料的存取很少发生同时存取的问题,因而不作资料库层次上的锁定,为了维护正确的资料,乐观锁定使用应用程式上的逻辑实现版本控制的解决。在不实行悲观锁定策略的情况下,资料不一致的情况一但发生,就会造成资料不一致。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的值,那么保存时,数据一样会被成功保存,这样锁定机制就有问题了,在设计时要注意。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值