首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >JPA和乐观锁定模式

JPA和乐观锁定模式
EN

Stack Overflow用户
提问于 2012-11-27 09:59:01
回答 3查看 24.5K关注 0票数 25

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

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

OPTIMISTIC 模式:

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

OPTIMISTIC_FORCE_INCREMENT 模式:

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

这似乎很明显,但我应该在什么时候使用OPTIMISTICOPTIMISTIC_FORCE_INCREMENT模式?我看到的唯一条件是,当我希望事务优先于其他事务时,应用OPTIMISTIC_FORCE_INCREMENT模式,因为选择此模式将回滚所有其他正在运行的事务(如果我完全理解机制的话)。

选择此模式而不是OPTIMISTIC模式还有其他原因吗?

谢谢

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-11-27 15:09:06

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

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

什么时候这样做取决于应用程序和用例。乐观将确保在提交时未更新其他对象。OPTIMISTIC_FORCE_INCREMENT将确保其他对象未被更新,并将在提交时增加其版本。

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

票数 15
EN

Stack Overflow用户

发布于 2014-08-26 13:48:40

别被这么长的回答吓到了。这个话题并不简单。

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

读提交要求不存在的脏读现象。简单地说,T1只能看到T2在提交T2之后所做的更改。

在JPA中使用乐观锁定可以将隔离级别提高到Repetable reads。

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

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

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

  • LockModeType.OPTIMISTIC (LockModeType.READ in JPA1.0)
  • LockModeType.OPTIMISTIC_FORCE_INCREMENT (LockModeType.WRITE in JPA1.0)

这两者有什么区别?

让我举例说明一下这个Person实体。

代码语言:javascript
复制
@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。

没有任何锁定的

代码语言:javascript
复制
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)

乐观读锁

代码语言:javascript
复制
    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,并且我们希望保持完整性。让我举例说明约翰买了一辆新车。

乐观读锁

代码语言:javascript
复制
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();

乐观写锁

代码语言:javascript
复制
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的情况下使用LockMode- LockModeType.OPTIMISTIC,反之亦然。

票数 52
EN

Stack Overflow用户

发布于 2016-12-16 13:54:13

LockModeType.OPTIMISTIC存在check-then-act问题,所以最好将它与PESSIMISTIC_READPESSIMISTIC_WRITE耦合起来。

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

例如,您可以使用LockModeType.OPTIMISTIC_FORCE_INCREMENTLockModeType.PESSIMISTIC_FORCE_INCREMENT,以便使考虑到子实体更改的父实体版本故事

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13581603

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档