`
usenrong
  • 浏览: 516560 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

JPA 各种实体锁模式悲观锁乐观锁的区别

 
阅读更多

为了能够同步访问实体,JPA提供了2种锁机制。这两种机制都可以避免两个事务中的其中一个,在不知情的情况下覆盖另一个事务的数据。

通过实体锁,我们通常希望避免在两个并行事务中产生如下情形:

  1. Adam的事务读取数据 X
  2. Barbara的事务读取数据 X
  3. Adam的事务修改数据 X,并将其修改为 XA
  4. Adam的事务写入数据 XA
  5. Barbara的事务修改数据 X,并将其修改为 XB
  6. Barbara的事务写入数据 XB

结果是,Adam所做的修改完全被Barbara所覆盖掉了,但是Barbara对此却毫不知晓。像这样的情况通常被称为“脏读”。显然,我们希望的结果是Adam写入 XA,而Barbara需要在写入 XB之前检查对 XA 的修改。

乐观锁的工作原理

乐观锁基于的假设是实际中冲突很少发生,即使发生,抛出一个错误也比想办法避免它们更容易接受和简单。在乐观锁中,允许一个事务正确完成,但另一个事务需要抛出异常并回滚,并且必须被重新执行或者丢弃。

我们还以Adam和Barbara为例,下面是一个使用乐观锁可能发生的情形:

  1. Adam的事务读取数据 X
  2. Barbara的事务读取数据 X
  3. Adam的事务修改数据 X,并将其修改为 XA
  4. Adam的事务写入数据 XA
  5. Barbara的事务修改数据 X,并将其修改为 XB
  6. Barbara的事务试图写入数据 XB,但是收到一个错误
  7. Barbara需要读取数据 XA(或者重新开始一个新的事务)
  8. Barbara的事务修改数据 XA,并将其修改为 XAB
  9. Barbara的事务写入数据 XAB

如你所见,Barbara被强制要求检查Adam的修改,并且她可以选择继续修改Adam的结果并保存(合并修改)。最后的数据将同时包括Adam和Barbara的修改。

乐观锁完全由JPA控制。它需要在DB表中额外存储一个版本号列。它完全依靠于底层用来存储关系型数据的DB引擎来工作。

悲观锁的工作原理

对于某些人来说,悲观锁更容易接受。当事务需要修改一个可能被其他事务同时修改的实体时,事务会发起一个命令将实体锁住。所有的锁会持续到事务结束后再自动释放。

使用悲观锁的情形可能如下所示:

  1. Adam的事务读取数据 X
  2. Adam的事务锁住 X
  3. Barbara的事务希望读取数据 X,但是因为 X 已经被锁住,只好等待
  4. Adam的事务修改数据 X,并将其修改为 XA
  5. Adam的事务写入数据 XA
  6. Barbara的事务读取数据 XA
  7. Barbara的事务修改数据 XA,并将其修改为 XAB
  8. Barbara的事务写入数据 XAB

如你所见,Barbara又一次被强制的写入 XAB,同时也包含了Adam的修改。但是,这个方案与乐观锁完全不同——Barbara需要等待Adam的事务完成以后才能够读取数据。更甚的是,为了让该场景正确工作,我们需要在两个事务中都手动发起一个lock命令。(因为我们并不确定那个事务先运行,所以两个事务都需要在修改数据前先进行锁定)虽然乐观锁要为每个实体增加一个版本列,比悲观锁工作略多,但是之后我们不需要再在事务中发起锁操作了。JPA会自动完成所有的检查,我们只需要处理可能的异常即可。

悲观锁使用底层数据库提供的锁机制来锁住表中已有的记录。JPA需要知道如何触发这些锁,并且尚不能完全支持某些数据库。

即使是JPA规范中也说到,不需要提供PESSIMISTIC_READ(因为许多DB只支持WRITE锁):

允许JPA实现用LockModeType.PESSIMISTIC_WRITE来代替LockModeType.PESSIMISTIC_READ,但是反之不可。

JPA中可用的锁类型

首先,我想说,对于实体中有添加了@Version注解的列,JPA会自动对该实体使用乐观锁。你不需要使用任何锁命令。但是,你可以在任何时候发起一个以下类型的锁:

  1. LockModeType.Optimistic
    1. 这就是默认的锁类型。也是如ObjectDB所说通常被大家所忽略的锁类型。在我的印象中,只有在需要动态获取并传递锁类型时,才会用到它,即使我们很清楚最后的锁是OPTIMISTIC的。虽然这个例子不太恰当,但是一个好的API设计,即使是默认值也应该为其提供一个可选项。
    2. 示例:Java
LockModeType lockMode = resolveLockMode();
A a = em.find(A.class, 1, lockMode);
  1. LockModeType.OPTIMISTIC_FORCE_INCREMENT

    1. 这个选项很少被用到。但是如果你希望用另一个实体来锁住对当前实体的引用,就需要使用它。换句话说,即使当前实体没有被修改,但是其他实体可能因为当前实体被修改,你就可以用它来锁住对当前实体的引用。
    2. 示例:
      1. 假设我们有两个实体“书(Book)”和“书架(Shelf)”。我们可以将书添加到书架中,但是书不持有对其书架的引用。我们需要对所有移动书到其他书架的动作加锁,以避免一本书被放在2个书架上。为了锁住这个动作,光锁住当前的书架实体是不够的,因为书可能还没有放到某个书架上。锁住所有书架也不合理,因为他们在不同的事务中可能都是不同的。唯一合理的是锁住书实体本身,即使在我们这个例子中它并没有发生变化(因为它并不持有其书架的引用)。
  2. LockModeType.PESSIMISTIC_READ

    1. 这个模式类似于LockModeType.PESSIMISTIC_WRITE,但是有一点不同:如果没有事务对实体加写锁,那么就不能阻塞对该实体的读取。它还允许其他事务使用LockModeType.PESSIMISTIC_READ来加锁。WRITE锁和READ锁之间的区别,已经被这两篇文章(here (ObjectDB) 和 here (OpenJPA))很详细的说明了。但是,不仅因为规范中允许,而且许多实现也没有分开处理,所以该锁模式经常被等价于LockModeType.PESSIMISTIC_WRITE
  3. LockModeType.PESSIMISTIC_WRITE

    1. 这是LockModeType.PESSIMISTIC_READ的增强版。当WRITE锁发生时,JPA在数据库的帮助下,会阻止其他事务读取实体,而不像READ锁那样只禁止写入。
  4. LockModeType.PESSIMISTIC_FORCE_INCREMENT

    1. 这是另一个很少使用的锁模式。但是,它可以用来结合PESSIMISTICOPTIMISTIC时使用。在以下场景中,单独使用PESSIMISTIC_WRITE是无效的:

      1. 事务A使用乐观锁并读取实体E
      2. 事务B请求实体E上的WRITE锁
      3. 事务B提交并释放E上的锁
      4. 事务A更新E并提交
    2. 在步骤4中,如果事务B没有增加版本列的值,那么就无法阻止事务A覆盖B的修改。即使事务B使用的是悲观锁,锁模式LockModeType.PESSIMISTIC_FORCE_INCREMENT也会强制事务B更新版本号,并让事务A失败并抛出OptimisticLockException

为了发起一个指定类型的锁,JPA提供了以下方法:

你可以使用JPA中这两种锁机制中的任意一种。如果需要,也可以选择悲观锁类型PESSIMISTIC_FORCE_INCREMENT,把二者混起来用。

分享到:
评论

相关推荐

    jpa 2.0 lock機制介紹1

    JPA提供了多种悲观锁模式: 1. `pessimistic_read`:只允许读,不允许更新或删除。 2. `pessimistic_write`:不允许读、更新或删除。 3. `pessimistic_force_increment`:类似乐观锁的`optimistic_force_increment`...

    吊打面试官之乐观锁实现源码

    乐观锁是一种并发控制策略,与悲观锁相比,它在处理多线程并发时更为乐观,认为数据在大多数情况下不会发生冲突,因此不会像悲观锁那样在读取数据时立即进行加锁操作。在本篇文章中,我们将深入探讨乐观锁的概念、...

    JPA 2.0jar包及JPA2.0源代码

    9. **事务和并发**:JPA 2.0提供了丰富的事务管理选项,支持不同级别的并发控制策略,如Optimistic Locking(乐观锁)和Pessimistic Locking(悲观锁)。 10. **存储过程**:JPA 2.0允许调用数据库的存储过程,并将...

    JPA教程(全部为PDF格式的电子书)

    5. **异步和并发**:讨论并发控制策略,如乐观锁和悲观锁,以及在多线程环境下的最佳实践。 这三本书合在一起,将为读者提供一个全面的JPA学习路径,从基本概念到高级特性的应用,再到实战技巧,有助于提升Java...

    Pro JPA 2 Mastering the Java Persistence API free down

    (JPA1.0 只允许从一个 unique 结果集中反回单个记录 ) ,指定 query 结果集的最大值,访问各个供应商的底层实体对象 manager 或 query ,最后就是加入悲观锁 (JPA1.0 只支持乐观锁 ) .  JPQL 也提供类似于 SQL...

    Hibernate annotation JPA 一对多,多对多

    为了处理并发访问,Hibernate提供了乐观锁和悲观锁机制。乐观锁通常通过版本号或者时间戳字段实现,而悲观锁则依赖于数据库提供的锁定机制。在实体类中添加`@Version`注解可以启用乐观锁。 以上内容详细解释了如何...

    hibernate-jpa-2.1-api-1.0.0.final.jar.zip

    - **多版本并发控制**: JPA 2.1引入了乐观锁和悲观锁策略,用于解决多线程环境下的数据一致性问题。 - **级联操作**: 支持在实体之间定义级联操作,如删除一个实体时自动删除与之关联的其他实体。 - **延迟加载**:...

    Spring Data JPA1.7中文文档

    支持乐观锁和悲观锁机制,以处理并发控制。 3.7. **审计** Spring Data JPA 可以与 JPA 审计框架(如 Hibernate Envers)集成,提供数据变更的追踪。 **Spring Data 扩展** 2.7. **Spring Data 扩展** 框架提供了...

    jpademo2.rar

    为了处理并发问题,JPA提供了乐观锁和悲观锁机制。乐观锁基于版本号或时间戳,悲观锁则在查询时锁定记录。 10. **实体关系映射** JPA允许定义不同类型的实体关系,如一对一(OneToOne)、一对多(OneToMany)、多...

    jpa 所要的jar包

    同时,了解和熟悉JPA的缓存机制、乐观锁和悲观锁、级联操作等高级特性,能够帮助你写出更加优化的代码。 总的来说,JPA作为ORM的一种解决方案,极大地简化了Java应用与数据库之间的交互,提高了开发效率。在使用...

    SpringDataJPA快速使用.pdf

    *乐观锁:使用 @Version 注解,在实体类上指定版本号字段 *悲观锁:使用 @Lock 注解,在实体类上指定锁机制 *select for update 语句:在查询语句中添加锁机制 应用场景和优点 SpringDataJPA 的应用场景非常广泛,...

    JPA2.0 javax.persistence src 源码

    8. **乐观锁与悲观锁**:JPA提供了乐观锁(通过版本字段或时间戳实现)和悲观锁(通过数据库锁定机制)来控制并发更新,确保数据一致性。 9. **事件监听**:JPA 2.0引入了生命周期事件监听器,允许开发者在实体的...

    good code for jpa example

    `JPA extend demo example`可能包含了上述概念的扩展,如多对多关系、复合主键、自定义查询、事务管理、乐观锁、悲观锁等高级主题的示例。通过这份示例,开发者可以学习到如何在实际项目中灵活运用JPA,提高开发效率...

    Spring Data JPA 中文版

    6. **锁**:提供乐观锁和悲观锁机制,用于控制并发访问时的数据一致性问题。 7. **审计**:支持记录实体对象的创建和修改历史,对于日志记录和版本控制非常有用。 综上所述,Spring Data JPA 通过提供一套强大的...

    Spring Data JPA中文文档[1.4.3].pdf

    - **锁机制**: 提供了乐观锁和悲观锁两种方式来处理并发访问的问题。 ##### 3.7 审计 - **基础知识**: 包括如何启用审计支持以及使用注解来标记需要审计的实体。 - **审计织入**: 使用 AOP(面向切面编程)来实现...

    spring-data-jpa-reference.pdf

    - **锁定实体**:支持乐观锁和悲观锁,确保并发访问时的数据安全。 #### 2.7 审计支持 - **基本审计配置**:提供自动记录实体创建和修改时间等功能。 - **注解基审计元数据**:使用`@CreatedDate`, `@LastModified...

    spring-data-jpa-reference1.3.pdf

    介绍了如何使用乐观锁或悲观锁来实现并发控制。 #### 2.6 Auditing - **基础**: 提供了审计功能的基础介绍。 - **Annotation-based auditing metadata**: 可以使用`@CreatedDate`, `@LastModifiedDate`等注解来...

    spring data jpa1.7 中文

    ### Spring Data JPA 1.7 核心知识点解析 #### 一、项目元数据 - **版本控制**: 项目的版本控制托管于 GitHub (http://github.com/spring-projects/spring-data-jpa),用户可以通过该链接查看源码、提交代码等。 -...

    How to Use Hibernate JPA

    - 在实体类中使用`@Version`注解来实现乐观锁。 - 或者在执行更新操作时使用`LockModeType.PESSIMISTIC_WRITE`来实现悲观锁。 #### 建立 Updateuser 功能 1. **创建 UpdateUser 功能** - 创建`UserService`类,...

Global site tag (gtag.js) - Google Analytics