Hibernate_ Pessimistic Lock_悲观锁

Hibernate_ Pessimistic Lock_悲观锁

http://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html_single/#transactions-locking

It is not intended(有意的) that users spend much time worrying about locking strategiesIt 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.

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:

  • LockMode.WRITE is acquired automatically(自动地) when Hibernate updates or inserts a row.

  • 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.

  • 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.

  • 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.

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 的悲观锁做一个测试

LockMode.UPGRADE

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。


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.

/**
 * 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的缓存中。


使用session.load和session.lock方法

/**
 * 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 方法。。


使用session.buildLockRequest

@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=====================

转载于:https://my.oschina.net/xinxingegeya/blog/351802

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值