23

我在 Oracle的博客上阅读了一篇关于 JPA 和锁定模式的文章。

我不完全理解OPTIMISTICOPTIMISTIC_FORCE_INCREMENT锁定模式类型之间的区别。

OPTIMISTIC模式 :

在此处输入图像描述

当用户使用此模式锁定实体时@version,事务开始时会检查版本字段实体 (),事务结束时也会检查版本字段。如果版本不同,则事务回滚。

OPTIMISTIC_FORCE_INCREMENT模式 :

在此处输入图像描述

当用户选择此模式时,他必须将 EntityManager 的状态刷新()到数据库中以手动增加版本字段。因此,所有其他乐观事务都将失效(回滚)。在事务结束时也会检查版本以提交或回滚事务。

看起来很清楚,但我什么时候应该使用OPTIMISTICvsOPTIMISTIC_FORCE_INCREMENT模式?我看到的唯一标准是OPTIMISTIC_FORCE_INCREMENT当我希望事务优先于其他事务时应用模式,因为选择此模式将回滚所有其他正在运行的事务(如果我理解机制的话)。

还有其他理由选择这种模式而不是OPTIMISTIC模式吗?

谢谢

4

3 回答 3

50

不要被这个冗长的答案吓到。这个话题不简单。

默认情况下 ,如果您未指定任何锁定(与 using 相同的行为),JPA 会强制执行读取提交LockModeType.NONE隔离级别。

提交读要求不存在脏读现象。只是 T1 只能在 T2 提交后看到 T2 所做的更改。

在 JPA 中使用乐观锁定将隔离级别提高到可 重复读取

如果 T1 在事务开始和结束时读取了一些数据,可重复读取可确保 T1 看到相同的数据,即使 T2 更改了数据并在 T1 中间提交。

棘手的部分来了。JPA以最简单的方式实现可重复读取:通过防止 不可重复读取现象。JPA 不够复杂,无法保留读取的快照。它只是通过引发异常来防止发生第二次读取(如果数据已从第一次读取发生更改)。

您可以从两个乐观锁定选项中进行选择:

  • LockModeType.OPTIMISTICLockModeType.READ在 JPA 1.0 中)

  • LockModeType.OPTIMISTIC_FORCE_INCREMENTLockModeType.WRITE在 JPA 1.0 中)

两者有什么区别?

Person让我用这个实体的例子来说明。

@Entity
public class Person {
    @Id int id;
    @Version int version;
    String name;
    String label;
    @OneToMany(mappedBy = "person", fetch = FetchType.EAGER)
    List<Car> cars;
    // getters & setters
}

现在假设我们有一个名为John的 Person存储在数据库中。我们在 T1 中读取了这个人,但在第二个事务 T2 中将他的名字改为Mike

没有任何锁定

Person person1 = em1.find(Person.class, id, LockModeType.NONE); //T1 reads Person("John")

Person person2 = em2.find(Person.class, id); //T2 reads Person("John")
person2.setName("Mike"); //Changing name to "Mike" within T2
em2.getTransaction().commit(); // T2 commits

System.out.println(em1.find(Person.class, id).getName()); // prints "John" - entity is already in Persistence cache
System.out.println(
  em1.createQuery("SELECT count(p) From Person p where p.name='John'")
    .getSingleResult()); // prints 0 - ups! don't know about any John (Non-repetable read)

乐观读锁

    Person person1 = em1.find(Person.class, id, LockModeType.OPTIMISTIC); //T1 reads Person("John")

    Person person2 = em2.find(Person.class, id); //T2 reads Person("John")
    person2.setName("Mike"); //Changing name to "Mike" within T2
    em2.getTransaction().commit(); // T2 commits

    System.out.println(
            em1.createQuery("SELECT count(p) From Person p where p.name='John'")
                    .getSingleResult()); // OptimisticLockException - The object [Person@2ac6f054] cannot be updated because it has changed or been deleted since it was last read. 


LockModeType.OPTIMISTIC_FORCE_INCREMENT当对其他实体(可能是非拥有关系)进行更改并且我们希望保持完整性时使用。让我以约翰购买一辆新车为例。

乐观读锁

Person john1 = em1.find(Person.class, id); //T1 reads Person("John")

Person john2 = em2.find(Person.class, id, LockModeType.OPTIMISTIC); //T2 reads Person("John")
//John gets a mercedes
Car mercedes = new Car();
mercedes.setPerson(john2);
em2.persist(mercedes);
john2.getCars().add(mercedes);
em2.getTransaction().commit(); // T2 commits

//T1 doesn't know about John's new car. john1 in stale state. We'll end up with wrong info about John.
if (john1.getCars().size() > 0) { 
    john1.setLabel("John has a car");
} else {
    john1.setLabel("John doesn't have a car");
}
em1.flush();

乐观写锁

Person john1 = em1.find(Person.class, id); //T1 reads Person("John")
Person john2 = em2.find(Person.class, id, LockModeType.OPTIMISTIC_FORCE_INCREMENT); //T2 reads Person("John")
//John gets a mercedes
Car mercedes = new Car();
mercedes.setPerson(john2);
em2.persist(mercedes);
john2.getCars().add(mercedes);
em2.getTransaction().commit(); // T2 commits

//T1 doesn't know about John's new car. john1 in stale state. That's ok though because proper locking won't let us save wrong information about John.
if (john1.getCars().size() > 0) { 
    john1.setLabel("John has a car");
} else {
    john1.setLabel("John doesn't have a car");
}
em1.flush(); // OptimisticLockException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)

尽管在 JPA 规范中有以下注释,但 Hibernate 和 EclipseLink 表现良好,不要使用它。

对于版本化对象,允许实现在请求 LockModeType.OPTIMISTIC 的地方使用 LockModeType.OPTIMISTIC_FORCE_INCREMENT,但反之则不行。

于 2014-08-26T13:48:40.797 回答
15

通常,您永远不会使用 lock() API 进行乐观锁定。JPA 将自动检查任何更新或删除的任何版本列。

用于乐观锁定的 lock() API 的唯一目的是当您的更新依赖于另一个未更改/更新的对象时。如果其他对象发生更改,这允许您的事务仍然失败。

何时执行此操作取决于应用程序和用例。OPTIMISTIC 将确保在您提交时其他对象尚未更新。OPTIMISTIC_FORCE_INCREMENT 将确保其他对象尚未更新,并将在提交时增加其版本。

乐观锁定总是在提交时验证,并且在提交之前不能保证成功。您可以使用 flush() 提前强制数据库锁定,或触发较早的错误。

于 2012-11-27T15:09:06.587 回答
4

LockModeType.OPTIMISTIC遇到check-then-act问题,因此最好将其与 aPESSIMISTIC_READPESSIMISTIC_WRITE.

另一方面,LockModeType.OPTIMISTIC_FORCE_INCREMENT不会遇到任何数据不一致问题,并且您通常会在修改子实体时使用它来控制父实体的版本。

例如,您可以使用LockModeType.OPTIMISTIC_FORCE_INCREMENTorLockModeType.PESSIMISTIC_FORCE_INCREMENT使父实体版本也考虑到子实体的变化

于 2016-12-16T13:54:13.883 回答