首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

对Hibernate的LockMode的更进一步思考

2012-09-01 
对Hibernate的LockMode的进一步思考让我们先来看看Hibernate的文档时怎么说的,关于LockMode: LockMode FOR

对Hibernate的LockMode的进一步思考

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


LockMode FORCE
????????? Similiar to UPGRADE except that, for versioned entities, it results in a forced version increment.


LockMode NONE
????????? No lock required.


LockMode READ
????????? A shared lock. Objects in this lock mode were read from the database in the current transaction, rather than being pulled from a cache (注:也就是从数据库中读数据,绕过了Hibernate的Cache)


LockMode UPGRADE
????????? An upgrade lock.(注:相当于SQL语句select xxx from xxxx for update,也就是把事务的处理交给了数据库)

LockMode UPGRADE_NOWAIT
????????? Attempt to obtain an upgrade lock, using an Oracle-style select for update nowait.


LockMode WRITE
????????? A WRITE lock is obtained when an object is updated or inserted.This lock mode is for internal use only and is not a valid mode for load() or lock() (both of which throw exceptions if WRITE is specified). (注:不能在load的时候用,否则抛出异常)


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

先看代码:

?

package com.javaye;   import org.hibernate.LockMode;   import org.hibernate.Session;   import org.hibernate.Transaction;   import com.javaye.models.Article;   public class Main {   private static void insert(){      Session session = HibernateSessionFactory.getSession();      Transaction tx = session.beginTransaction();      Article art = new Article();      art.setTitle("AAA");      art.setVisitAmount(0);      session.saveOrUpdate(art);      tx.commit();   }   private static void update(){      Session session = HibernateSessionFactory.getSession();      System.out.println("session:"+session.hashCode());      Transaction tx = session.beginTransaction();   <SPAN style="COLOR: #ff0000">Article art = (Article) session.load(Article.class, 1,LockMode.UPGRADE);      System.out.println("             loaded");</SPAN>   <SPAN style="COLOR: #0000ff">art.setVisitAmount(art.getVisitAmount()+1);</SPAN>      session.save(art);      tx.commit();      session.evict(art);        }   private static void work(){      for(int i=0;i<10;i++){       System.out.println(Thread.currentThread().getName()+":"+(i+1)+"times.");       update();      }   }   public static void main(String[] args) throws Exception{      Thread t1 = new Thread(       new Runnable(){        public void run(){         work();        }       }      );           Thread t2 = new Thread(        new Runnable(){         public void run(){          work();         }        }      );      t1.setName("Thread1");      t2.setName("Thread2");      t1.setDaemon(true);      t2.setDaemon(true);      t1.start();      t2.start();      t1.join();      t2.join();        }   } package com.javaye;import org.hibernate.LockMode;import org.hibernate.Session;import org.hibernate.Transaction;import com.javaye.models.Article;public class Main {private static void insert(){   Session session = HibernateSessionFactory.getSession();   Transaction tx = session.beginTransaction();   Article art = new Article();   art.setTitle("AAA");   art.setVisitAmount(0);   session.saveOrUpdate(art);   tx.commit();}private static void update(){   Session session = HibernateSessionFactory.getSession();   System.out.println("session:"+session.hashCode());   Transaction tx = session.beginTransaction();Article art = (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);}private static void work(){   for(int i=0;i<10;i++){    System.out.println(Thread.currentThread().getName()+":"+(i+1)+"times.");    update();   }}public static void main(String[] args) throws Exception{   Thread t1 = new Thread(    new Runnable(){     public void run(){      work();     }    }   );   Thread t2 = new Thread(     new Runnable(){      public void run(){       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的情况下,如果一个事务获得了锁,即使另外的事务想读取数据也是不行的,必须等待锁的释放。

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

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

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

热点排行