【问题标题】:Hibernate JPA - PESSIMISTIC.WRITE not workingHibernate JPA - PESSIMISTIC.WRITE 不工作
【发布时间】:2013-10-02 22:19:29
【问题描述】:

我正在使用 JPA 2.0 的 Hibernate 实现在表行中创建一个计数器。我正在使用带有 InnoDB 引擎的 MySQL 5.5。我正在尝试锁定计数器行,以便 JVM 之外的任何进程都无法查看该计数器,直到我的代码将其递增。我的代码如下所示:

  //inside a Transaction....

  //key is an enum
  final PropertyKey key = PropertyKey.DEPLOY_COUNTER;
  final Query query =
     entityManager.createQuery("FROM Property s where propertyKey = :key").setParameter("key", key);
  query.setLockMode(LockModeType.PESSIMISTIC_WRITE);
  LOG.debug("Blocking (maybe) while waiting to update deploy counter");
  final Property counterAsProperty = (Property) query.getSingleResult();

  try
  {
     Thread.sleep(15000);
     //while sleeping I use MySQL cli to check value of property in database
  }
  catch (InterruptedException e)
  {
     e.printStackTrace();
  }

  //in java, increment counter by one and then save in db
  //...

我使用 Thread.sleep() 在事务中间暂停代码。当线程处于休眠状态时,我使用 MySQL CLI 客户端登录到数据库并检查属性的值。该会话看起来像这样:

user@mypc [user]> begin work;
Query OK, 0 rows affected (0.00 sec)

user@mypc [user]> select * from property where property_key = 'DEPLOY_COUNTER';
+----+---------+-----------+--------------------+----------------+
| id | version | encrypted | property_key       | property_value |
+----+---------+-----------+--------------------+----------------+
| 10 |       0 |         0 | DEPLOY_COUNTER     | 66             |   
+----+---------+-----------+--------------------+----------------+

请注意查询是如何立即返回的(0.00 秒),因为我预计它会阻塞,直到线程退出睡眠并且事务完成。我对 LockModeType.PESSIMISTIC_WRITE 的理解是,它应该把检索到的行放在一个独占锁中,不能被另一个事务读取或写入。

注意:当线程处于休眠状态时,对此行的更新确实会阻塞。

如果我设置了 PESSIMISTIC_WRITE 锁定模式,为什么另一个 DB 连接可以在另一个事务正在运行时查看数据?

【问题讨论】:

    标签: java mysql hibernate jpa locking


    【解决方案1】:

    默认的 InnoDB 隔离级别是 REPEATABLE READ,它允许没有排他锁的事务读取(但不更新)锁定的记录。这允许其他事务对记录进行非脏读。 SERIALIZABLE 似乎可以防止对锁定记录的任何读取。您可以在使用hibernate.connection.isolation 设置休眠连接属性时设置隔离级别,尽管这会影响所有连接,而不是我愿意支付的成本。这篇 SO 帖子指出了一种按连接执行此操作的方法,但该方法需要不推荐使用的方法:JPA and MySQL transaction isolation level

    我最终使用了其中一个

    UPDATE counter SET value = LAST_INSERT_ID(value + 1);
    SELECT LAST_INSERT_ID();
    

    此处引用的方法:http://tedyoung.me/2011/04/14/jpa-counters-and-sequences/,不需要任何锁定。

    【讨论】:

      【解决方案2】:

      对于选择查询,这不起作用

      如果您查询“选择更新”,它将起作用 那么您的查询将等到您的应用程序不释放锁定的时间

      select * from property where property_key = 'DEPLOY_COUNTER' for update;
      

      上面的查询会一直等到锁被释放。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-12-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-01-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多