Hibernate_ Pessimistic Lock_悲觀鎖html
http://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html_single/#transactions-lockingjava
It is not intended(有意的) that users spend much time worrying about locking strategies. It is usually enough to specify an isolation level for the JDBC connections and then simply let the database do all the work. However, advanced users may wish to obtain exclusive pessimistic locks or re-obtain locks at the start of a new transaction.sql
Hibernate will always use the locking mechanism of the database(使用數據庫的鎖機制); it never lock objects in memory.數據庫
The LockMode class defines the different lock levels that can be acquired by Hibernate. A lock is obtained by the following mechanisms:express
LockMode.WRITE is acquired automatically(自動地) when Hibernate updates or inserts a row.api
LockMode.UPGRADE can be acquired upon explicit(明確的) user request using SELECT ... FOR UPDATE on databases which support that syntax.緩存
LockMode.UPGRADE_NOWAIT can be acquired upon explicit user request using a SELECT ... FOR UPDATE NOWAIT under Oracle.session
LockMode.READ is acquired automatically when Hibernate reads data under Repeatable Read or Serializable isolation level. It can be re-acquired by explicit user request.app
LockMode.NONE represents the absence(缺席,缺勤) of a lock. All objects switch to(切換到) this lock mode at the end of a Transaction. Objects associated with the session via a call to update() or saveOrUpdate() also start out in this lock mode.less
The "explicit user request" is expressed in one of the following ways:
A call to Session.load(), specifying a LockMode.
A call to Session.lock().
A call to Query.setLockMode().
If Session.load() is called with UPGRADE or UPGRADE_NOWAIT, and the requested object was not yet loaded by the session, the object is loaded using SELECT ... FOR UPDATE. If load() is called for an object that is already loaded with a less restrictive lock than the one requested, Hibernate calls lock() for that object.
Session.lock() performs a version number check if the specified lock mode is READ, UPGRADE or UPGRADE_NOWAIT. In the case of UPGRADE or UPGRADE_NOWAIT, SELECT ... FOR UPDATE is used.
If the requested lock mode is not supported by the database, Hibernate uses an appropriate alternate mode instead of throwing an exception. This ensures that applications are portable.
LockMode枚舉類
public enum LockMode { /** * No lock required. If an object is requested with this lock * mode, a <tt>READ</tt> lock will be obtained if it is * necessary to actually read the state from the database, * rather than pull it from a cache.<br> * <br> * This is the "default" lock mode. */ NONE( 0 ), /** * A shared lock. Objects in this lock mode were read from * the database in the current transaction, rather than being * pulled from a cache. */ READ( 5 ), /** * An upgrade lock. Objects loaded in this lock mode are * materialized using an SQL <tt>select ... for update</tt>. * * @deprecated instead use PESSIMISTIC_WRITE */ @Deprecated UPGRADE( 10 ), /** * Attempt to obtain an upgrade lock, using an Oracle-style * <tt>select for update nowait</tt>. The semantics of * this lock mode, once obtained, are the same as * <tt>UPGRADE</tt>. */ UPGRADE_NOWAIT( 10 ), /** * Attempt to obtain an upgrade lock, using an Oracle-style * <tt>select for update skip locked</tt>. The semantics of * this lock mode, once obtained, are the same as * <tt>UPGRADE</tt>. */ UPGRADE_SKIPLOCKED( 10 ), /** * A <tt>WRITE</tt> 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 <tt>load()</tt> or <tt>lock()</tt> (both * of which throw exceptions if WRITE is specified). */ WRITE( 10 ), /** * Similar to {@link #UPGRADE} except that, for versioned entities, * it results in a forced version increment. * * @deprecated instead use PESSIMISTIC_FORCE_INCREMENT */ @Deprecated FORCE( 15 ), /** * start of javax.persistence.LockModeType equivalent modes */ /** * Optimistically assume that transaction will not experience contention for * entities. The entity version will be verified near the transaction end. */ OPTIMISTIC( 6 ), /** * Optimistically assume that transaction will not experience contention for * entities. The entity version will be verified and incremented near the transaction end. */ OPTIMISTIC_FORCE_INCREMENT( 7 ), /** * Implemented as PESSIMISTIC_WRITE. * TODO: introduce separate support for PESSIMISTIC_READ */ PESSIMISTIC_READ( 12 ), /** * Transaction will obtain a database lock immediately. * TODO: add PESSIMISTIC_WRITE_NOWAIT */ PESSIMISTIC_WRITE( 13 ), /** * Transaction will immediately increment the entity version. */ PESSIMISTIC_FORCE_INCREMENT( 17 ); private final int level; private LockMode(int level) { this.level = level; } /** * Check if this lock mode is more restrictive than the given lock mode. * * @param mode LockMode to check * * @return true if this lock mode is more restrictive than given lock mode */ public boolean greaterThan(LockMode mode) { return level > mode.level; } /** * Check if this lock mode is less restrictive than the given lock mode. * * @param mode LockMode to check * * @return true if this lock mode is less restrictive than given lock mode */ public boolean lessThan(LockMode mode) { return level < mode.level; } }
下面針對MySQL對Hibernate 的悲觀鎖作一個測試
can be acquired upon explicit(明確的) user request using SELECT ... FOR UPDATE on databases which support that syntax.
@Before public void beginTransaction() { this.userDao = new UserDao(); this.roleDao = new RoleDao(); this.workDao = new WorkDao(); HibernateUtil.getSessionFactory().getCurrentSession() .beginTransaction(); System.out.println("before=" + HibernateUtil.getSessionFactory() .getCurrentSession().hashCode()); } @Test public void test892() { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); Query query = session.createQuery("from Worker as w where id = 1"); query.setLockMode("w", LockMode.UPGRADE); Worker worker = (Worker) query.uniqueResult(); worker.setAge(10); System.out.println(worker.toString()); System.out.println("commit=" + HibernateUtil.getSessionFactory() .getCurrentSession().hashCode()); HibernateUtil.getSessionFactory().getCurrentSession().getTransaction() .commit(); System.out.println("commit end"); }
其中,query.setLockMode("w", LockMode.UPGRADE);設置鎖的模式爲UPGRADE,運行看結果,以下
before=209429254 Hibernate: select worker0_.id as id1_2_, worker0_.age as age2_2_, worker0_.name as name3_2_, worker0_.version as version4_2_ from tb_worker worker0_ where worker0_.id=1 for update Worker{id=1, name='Thread-2', age=10, version=34} commit=209429254 Hibernate: update tb_worker set age=?, name=?, version=? where id=? and version=? commit end Process finished with exit code 0
注意其發出的sql語句形式爲select for update,給一個record加上了排它鎖,也就是X鎖,也就是write lock。
is acquired automatically when Hibernate reads data under Repeatable Read or Serializable isolation level. It can be re-acquired by explicit user request.
/** * LockMode.READ */ @Test public void test8921() { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); Query query = session.createQuery("from Worker as w where id = 1"); query.setLockMode("w", LockMode.READ); Worker worker = (Worker) query.uniqueResult(); // worker.setAge(8); System.out.println(worker.toString()); System.out.println("commit=" + HibernateUtil.getSessionFactory() .getCurrentSession().hashCode()); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("sleep end"); HibernateUtil.getSessionFactory().getCurrentSession().getTransaction() .commit(); System.out.println("commit end"); }
LockMode.READ,在api文檔中是這樣說的,
/**
* A shared lock. Objects in this lock mode were read from
* the database in the current transaction, rather than being
* pulled from a cache.
*/
READ( 5 ),
運行結果:
before=209429254 Hibernate: select worker0_.id as id1_2_, worker0_.age as age2_2_, worker0_.name as name3_2_, worker0_.version as version4_2_ from tb_worker worker0_ where worker0_.id=1 Worker{id=1, name='s', age=870, version=36} commit=209429254 sleep end commit end
發出的sql就是普通的select查詢,不是select ... lock in share mode的形式。因此對於數據庫根本就沒有鎖,只不過是從數據庫讀數據,而不會從Hibernate的緩存中。
/** * LockMode.UPGRADE */ @Test public void test98766() { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); Worker worker = (Worker) session.load(Worker.class, 1); session.lock(worker, LockMode.UPGRADE); System.out.println(worker.toString()); System.out.println("commit=" + HibernateUtil.getSessionFactory() .getCurrentSession().hashCode()); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("sleep end"); HibernateUtil.getSessionFactory().getCurrentSession().getTransaction() .commit(); System.out.println("commit end"); }
Worker worker = (Worker) session.load(Worker.class, 1);
session.lock(worker, LockMode.UPGRADE);
這兩個方法就把那一條記錄鎖住了,看一下運行結果,
before=209429254 Hibernate: select worker0_.id as id1_2_0_, worker0_.age as age2_2_0_, worker0_.name as name3_2_0_, worker0_.version as version4_2_0_ from tb_worker worker0_ where worker0_.id=? Hibernate: select id from tb_worker where id =? and version =? for update Worker{id=1, name='231', age=870, version=36} commit=209429254 sleep end commit end Process finished with exit code 0
這個結果頗有意思,select id from tb_worker where id =? and version =? for update,是經過這個sql 把那條記錄鎖住的,而不是直接鎖住的,由於是load 方法。。
@Test public void test766() { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); Worker worker = (Worker) session.get(Worker.class, 1); session.buildLockRequest(new LockOptions(LockMode.UPGRADE)).lock(worker); worker.setName("jjjjjjjjjjjjjj"); System.out.println(session.getCurrentLockMode(worker)); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("sleep end"); System.out.println("commit=" + HibernateUtil.getSessionFactory() .getCurrentSession().hashCode()); HibernateUtil.getSessionFactory().getCurrentSession().getTransaction() .commit(); System.out.println("commit end"); }
測試結果:
before=209429254 Hibernate: select worker0_.id as id1_2_0_, worker0_.age as age2_2_0_, worker0_.name as name3_2_0_, worker0_.version as version4_2_0_ from tb_worker worker0_ where worker0_.id=? Hibernate: select id from tb_worker where id =? and version =? for update UPGRADE sleep end commit=209429254 Hibernate: update tb_worker set age=?, name=?, version=? where id=? and version=? commit end Process finished with exit code 0
經過結果,lock(obj)是經過select id from tb_worker where id =? and version =? for update 把record鎖住。
以上就是對Hibernate 悲觀鎖的實踐
=====================END=====================