【问题标题】:Difference between LockModeType JpaLockModeType Jpa 的区别
【发布时间】:2016-01-08 20:10:46
【问题描述】:

我对 JPA 中 LockModeTypes 的工作感到困惑:

  1. LockModeType.Optimistic

    • 它在提交时增加版本。
    • 这里的问题是:如果我的实体中有版本列,并且如果我没有指定此锁定模式,那么它的工作方式也类似,那么它有什么用?
  2. LockModeType.OPTIMISTIC_FORCE_INCREMENT

    • 即使实体没有更新,它也会增加版本列。
    • 但是,如果任何其他进程在提交此事务之前更新了同一行,那么它有什么用呢?这笔交易无论如何都会失败。那么这个LockModeType有什么用。
  3. LockModeType.PESSIMISTIC_READ

    • 此锁定模式发出select for update nowait(如果未指定提示超时)..
    • 所以基本上这意味着在提交该事务之前没有其他事务可以更新该行,那么它基本上是一个写锁,为什么它命名为Read锁?
  4. LockModeType.PESSIMISTIC_WRITE

    • 此锁定模式还会发出select for update nowait(如果未指定提示超时)。
    • 这里的问题是这种锁定模式和LockModeType.PESSIMISTIC_READ 之间有什么区别,因为我看到两者都会触发相同的查询?
  5. LockModeType.PESSIMISTIC_FORCE_INCREMENT

    • 这会执行select for update nowait(如果未指定提示超时),还会增加版本号。
    • 我完全没有使用它。
    • 如果有for update no wait,为什么需要版本增量?

【问题讨论】:

    标签: oracle hibernate jpa concurrency transactions


    【解决方案1】:

    我首先要区分乐观锁和悲观锁,因为它们的底层机制不同。

    乐观锁定完全由 JPA 控制,只需要 DB 表中的附加版本列。它完全独立于用于存储关系数据的底层数据库引擎。

    另一方面,悲观锁定使用底层数据库提供的锁定机制来锁定表中的现有记录。 JPA 需要知道如何触发这些锁,而某些数据库不支持或仅部分支持。

    现在是锁类型列表:

    1. LockModeType.Optimistic
      • 如果实体指定版本字段,则这是默认值。对于没有版本列的实体,不能保证使用这种类型的锁适用于任何 JPA 实现。如ObjectDB 所述,此模式通常被忽略。在我看来,它的存在只是为了让您可以动态地计算锁定模式并进一步传递它,即使锁定最终是 OPTIMIISTIC 也是如此。虽然不太可能的用例,但提供一个选项来引用甚至是默认值总是好的 API 设计。
    • 例子:

         `LockModeType lockMode = resolveLockMode();
       A a = em.find(A.class, 1, lockMode);`
      
    1. LockModeType.OPTIMISTIC_FORCE_INCREMENT
    • 这是一个很少使用的选项。但是,如果您想锁定另一个实体引用该实体,这可能是合理的。换句话说,即使实体没有被修改,您也希望锁定与实体的工作,但其他实体可能会与此实体相关地被修改。
    • 示例:我们有实体书架。可以将书添加到书架,但书没有对其书架的任何引用。锁定将书移动到书架的动作是合理的,这样一本书就不会在此事务结束之前(由于另一个事务)最终进入另一个书架。要锁定此操作,仅锁定当前书架实体是不够的,因为该书还不必在书架上。锁定所有目标书架也没有意义,因为它们在不同的事务中可能会有所不同。唯一有意义的是锁定 book 实体本身,即使在我们的例子中它没有被更改(它不持有对其书架的引用)。
    1. LockModeType.PESSIMISTIC_READ
    • 此模式类似于LockModeType.PESSIMISTIC_WRITE,但有一点不同:直到某个事务在同一个实体上设置写锁,它不应该阻塞读取该实体。它还允许使用LockModeType.PESSIMISTIC_READ 锁定其他事务。 here (ObjectDB)here (OpenJPA) 很好地解释了 WRITE 和 READ 锁之间的区别。如果一个实体已经被另一个事务锁定,任何锁定它的尝试都会抛出异常。此行为可以修改为等待一段时间释放锁,然后再抛出异常并回滚事务。为此,请指定javax.persistence.lock.timeout 提示以及在抛出异常之前等待的毫秒数。如Java EE tutorial 所述,有多种方法可以在多个级别上执行此操作。
    1. LockModeType.PESSIMISTIC_WRITE
    • 这是LockModeType.PESSIMISTIC_READ 的更强版本。当WRITE 锁到位时,JPA 在数据库的帮助下将阻止任何其他事务读取实体,而不仅仅是像READ 锁那样写入。
    • 没有规定如何在 JPA 提供程序中与底层 DB 合作实现这一点。在您使用 Oracle 的情况下,我会说 Oracle 不提供接近 READ 锁的东西。 SELECT...FOR UPDATE 确实是 WRITE 锁。这可能是休眠中的一个错误,或者只是一个决定,而不是实现自定义“更软”READ 锁,而是使用“更硬”WRITE 锁。这主要不会破坏一致性,但不会使用READ 锁定所有规则。您可以使用READ 锁和长时间运行的事务运行一些简单的测试,以确定是否有更多事务能够获取同一实体上的READ 锁。这应该是可能的,而WRITE 锁则不行。
    1. Lo​​ckModeType.PESSIMISTIC_FORCE_INCREMENT
    • 这是另一种很少使用的锁定模式。但是,它是您需要结合PESSIMISTICOPTIMISTIC 机制的选项。在以下情况下,使用普通的 PESSIMISTIC_WRITE 会失败:
      1. 事务 A 使用乐观锁定并读取实体 E
      2. 事务 B 获得实体 E 上的 WRITE 锁
      3. 事务 B 提交并释放 E 的锁
      4. 事务 A 更新 E 并提交
    • 在步骤4中,如果事务B没有增加版本列,没有什么可以阻止A覆盖B的更改。锁定模式LockModeType.PESSIMISTIC_FORCE_INCREMENT将强制事务B更新版本号并导致事务A失败OptimisticLockException,即使 B 使用的是悲观锁定。
    1. Lo​​ckModeType.NONE
    • 如果实体不提供版本字段,这是默认设置。这意味着没有启用锁定冲突将在尽力而为的基础上解决,并且不会被检测到。这是事务之外唯一允许的锁定模式

    【讨论】:

    • 我还找到了一篇较早的文章,其中提到“在撰写本文时,大多数持久性提供程序,例如 EclipseLink 和 Hibernate,都通过 PESSIMISTIC_WRITE 提供了对 PESSIMISTIC_READ 的支持。” (developer.com/java/ent/article.php/3897331/…)
    • 你去吧,我真的很好奇为什么有时悲观的 READ 表现得像 WRITE,我发现这直接写在 JPA 规范中:“允许实现使用 LockModeType.PESSIMISTIC_WRITE where LockModeType .PESSIMISTIC_READ 已请求,但反之则不然。”
    • 在第 2 点:“这样一本书就不会出现在 2 个书架上”。如果是这种情况,难道不会在书架中使用@OneToMany 关系(即一个书架可以有很多书)来表示这种约束吗?如果是这样,无论锁定模式如何,将同一本书添加到第二个书架都会失败,因为违反了 book_shelf 表中 id 唯一性的约束,不是吗?
    • @arcuri82,你是对的 - 显然,与 @OneToMany 关系,一本书不能放在 2 个书架上。但它仍然可能通过并行事务最终进入另一个货架,如果我们不使用 OPTIMISTIC_FORCE_INCREMENT,我们将在我们的事务中覆盖它。当我们使用它时,会正确抛出一个 OptimisticLockException 并随后进行回滚。我修复了我的示例,感谢您的反馈。
    • @OndrejM,您能否解释一下该示例如何适用于 LockModeType.OPTIMISTIC_FORCE_INCREMENT?假设 Book 的当前版本为 1。当事务 A 锁定 Book 实体时,将其版本增加到 2。新版本对其他事务是否可见?如果 db 隔离级别为 READ_COMMITTED 或更严格,则需要提交事务 A 以使新版本可见。同时,Transaction B 锁定了同一个 Book 实体,将其版本提高到 3,这如何保证该书只在一个书架上结束呢?谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-07
    • 2013-02-06
    • 1970-01-01
    • 1970-01-01
    • 2017-11-29
    • 2013-09-16
    相关资源
    最近更新 更多