JPA和乐观的锁定模式

时间:2012-11-27 09:59:01

标签: jpa transactions locking

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

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

OPTIMISTIC模式:

enter image description here

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

OPTIMISTIC_FORCE_INCREMENT模式:

enter image description here

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

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

是否有其他理由选择此模式而不是OPTIMISTIC模式?

由于

3 个答案:

答案 0 :(得分:43)

不要被这个长长的答案吓到。这个主题并不简单。

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

Read committed 需要不存在 Dirty read 现象。只有T1才能看到T2提交后T2所做的更改。

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

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

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

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

  • LockModeType.OPTIMISTIC(JPA 1.0中LockModeType.READ

  • LockModeType.OPTIMISTIC_FORCE_INCREMENT(JPA 1.0中LockModeType.WRITE

两者之间有什么区别?

让我举例说明这个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 的人。我们在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表现良好且不能使用它。

  

对于版本化对象,允许实现使用LockMode-   Type.OPTIMISTIC_FORCE_INCREMENT请求LockModeType.OPTIMISTIC,   但反之亦然。

答案 1 :(得分:12)

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

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

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

始终在提交时验证乐观锁定,并且在提交之前无法保证成功。您可以使用flush()提前强制数据库锁定,或触发先前的错误。

答案 2 :(得分:0)

正如this post中所述,LockModeType.OPTIMICTIC遇到了行为问题,因此您better off coupling it with a PESSIMISTIC_READ or PESSIMISTIC_WRITE

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

查看this article,详细了解如何使用LockModeType.OPTIMICTIC_FORCE_INCREMENTLockModeType.PESSIMISTIC_FORCE_INCREMENT,以便父实体版本也可以考虑子实体更改。