不要被这个冗长的答案吓到。这个话题不简单。
默认情况下 JPA 强加
如果您未指定任何锁定,读取已提交隔离级别(与使用 LockModeType.NONE 的行为相同)。
已提交读要求脏读现象不存在。只是 T1 只能在 T2 提交后看到 T2 所做的更改。
在 JPA 中使用乐观锁定将隔离级别提高到
可重复读取。
如果 T1 在事务开始和结束时读取一些数据,可重复读取确保 T1 看到相同的数据,即使 T2 更改数据并在 T1 中间提交。
这里是棘手的部分。 JPA 以最简单的方式实现可重复读取:通过防止 不可重复读取现象。 JPA 不够复杂,无法保留读取的快照。它只是通过引发异常来防止发生第二次读取(如果数据与第一次读取相比已更改)。
您可以从两个乐观锁定选项中进行选择:
两者有什么区别?
让我用这个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 被请求,
但反之亦然。