Hibernate的LockMod

让我们先来看看Hibernate的文档时怎么说的,关于LockMode:

LockModeFORCE

SimiliartoUPGRADEexceptthat,forversionedentities,itresultsinaforcedversionincrement.

LockModeNONE

Nolockrequired.

LockModeREAD

Asharedlock.Objectsinthislockmodewerereadfromthedatabaseinthecurrenttransaction,ratherthanbeingpulledfromacache(注:也就是从数据库中读数据,绕过了Hibernate的Cache)

LockModeUPGRADE

Anupgradelock.(注:相当于SQL语句selectxxxfromxxxxforupdate,也就是把事务的处理交给了数据库)

LockModeUPGRADE_NOWAIT

Attempttoobtainanupgradelock,usinganOracle-styleselectforupdatenowait.

LockModeWRITE

AWRITElockisobtainedwhenanobjectisupdatedorinserted.Thislockmodeisforinternaluseonlyandisnotavalidmodeforload()orlock()(bothofwhichthrowexceptionsifWRITEisspecified).(注:不能在load的时候用,否则抛出异常)

不过,“纸上得来终觉浅,觉知此事要躬行”,博主做了下实验来比较这些“锁”的不同。

先看代码:

packagecom.javaye;

importorg.hibernate.LockMode;

importorg.hibernate.Session;

importorg.hibernate.Transaction;

importcom.javaye.models.Article;

publicclassMain{

privatestaticvoidinsert(){

Sessionsession=HibernateSessionFactory.getSession();

Transactiontx=session.beginTransaction();

Articleart=newArticle();

art.setTitle("AAA");

art.setVisitAmount(0);

session.saveOrUpdate(art);

tx.commit();

}

privatestaticvoidupdate(){

Sessionsession=HibernateSessionFactory.getSession();

System.out.println("session:"+session.hashCode());

Transactiontx=session.beginTransaction();

Articleart=(Article)session.load(Article.class,1,LockMode.UPGRADE);

System.out.println("loaded");

art.setVisitAmount(art.getVisitAmount()+1);

session.save(art);

tx.commit();

session.evict(art);

}

privatestaticvoidwork(){

for(inti=0;i<10;i++){

System.out.println(Thread.currentThread().getName()+":"+(i+1)+"times.");

update();

}

}

publicstaticvoidmain(String[]args)throwsException{

Threadt1=newThread(

newRunnable(){

publicvoidrun(){

work();

}

}

);

Threadt2=newThread(

newRunnable(){

publicvoidrun(){

work();

}

}

);

t1.setName("Thread1");

t2.setName("Thread2");

t1.setDaemon(true);

t2.setDaemon(true);

t1.start();

t2.start();

t1.join();

t2.join();

}

}

这是一个多线程程序,每个线程都会从数据库中取出visit_amount,然后加一,再存回数据库,每个线程重复10遍。

请注意蓝色的部分,我们在这里设一个断点,那么用Eclipse调试的时候,到达这个断点的线程就会停下来,由于它的事务还没有commit(),LockMode.UPGRADE的锁就还没有释放,那么另外一个线程中事务就会在load的时候因为不能获得锁而阻塞,那么理论上我们只会看到只有一句“loaded”输出。实验结果证明了我的猜想,LockMode.UPGRADE的情况下,如果一个事务获得了锁,即使另外的事务想读取数据也是不行的,必须等待锁的释放。

那么,改写数据可以吗?笔者又做了一个实验,打开MySQLQueryBrowser,直接生改数据库,把visit_amount字段的值硬生生改过来,结果发现提交的时候就阻塞了,MySQL的海豚标志一个劲的游泳,这说明,LockMode.UPGRADE级别的锁不是由Hibernate控制的,而是由数据库控制的。

再试一试LockeMode.Read,断点还是设在原来的位置,发现有两次“loaded”输出,证明两个事务可以同时读取这条数据,那么这个锁有什么作用呢?根据我实验的结果,似乎只是为了绕过cache,从数据库直接读取。为了证明我的猜想,我直接通过MySQLQueryBrowser更改了visit_amount,调试发现,Hibernate是从数据库中读取的新值,而不是cache中的老值。

最后在补充一点,LockMode.UPGRADE加锁是有超时时间的,如果加锁后超过一定的时间不commit,Hibernate会抛出异常。

相关推荐