我有以下问题
我的服务是 Rest 控制器和 JPA 存储库之间的链接,在数据库中创建实体之前执行一些检查。 但是接下来的问题就出现了,如果客户端 1 和客户端 2 有一个共同的逻辑父级,同时发送创建实体的请求,也就是说,它们很可能同时通过检查,能够创建一个理论上不应该创建的实体,如何避免这个问题? 此外,如果客户具有不同的父级,则他们可以在任何情况下创建这些实体。
有一个想法可以解决这个问题,在创建实体之前,需要在创建实体之前在父行上获取一个锁,然后第二个客户端在尝试创建实体时会出现错误,但是如何在Spring JPA中实现这种方法?谢谢。
为了更好地理解,我将举一个例子:
public class Parent {
@Id
Long id;
@OneToMany
@JoinColumn(name = "parent_id")
private List<Child> childs;
}
请求创建实体的所有客户端都有一些父级 服务的近似代码和创建某些内容的方法:
public SomeEntity createSomeEntity(SomeEntity someEntity) {
// further, some checks are made on these lines
// checks
if (checks are passed) {
someEntityRepository.save(someEntity);
} else {
// entity will not be created in the database
}
}
如果客户端有不同的父项,则没有问题,但如果它们有一个父项,则假设两个客户端同时通过检查并创建冲突的实体。
同时,我不想使这段代码同步或每次使用悲观锁定创建 SomeEntity,因为这样对于具有不同祖先的客户端来说,多线程就会丢失。正如我所说,我的想法是,如果在签入服务类之前获取父行上的锁,那么我们不会失去并行性以及具有公共父级的客户端创建不应排除的实体的情况,但是如何使用 Spring JPA 我不知道
使用的PostgreSQL数据库版本12.2
如果我使用 SQL 代码执行此操作,在这个地方,我们将 SomeEntity 实体同步添加到客户端,其父级的 id = 1:
START TRANSACTION;
SELECT * FROM parent WHERE id = 1 FOR UPDATE;
-- here is the logic for adding SomeEntity
COMMIT;
在父项上启用乐观锁定,并在开始为父项创建子项时使用OPTIMISTIC_FORCE_INCREMENT
锁定模式锁定父项。
它将确保如果多个线程同时为同一父线程创建子线程,则只有一个线程会成功,而其他线程会失败。对于失败的,只需捕获OptimisticLockException
并重试。
请注意,如果另一个线程同时尝试更新父线程的内容,也会被视为冲突。所以从技术上讲,对于你的用例来说,它将确保只有一个线程将为同一个父线程创建子线程,或者修改同一个父线程。
代码方面,它看起来像:
@Entity
public class Parent {
@Version
private long version;
}
和锁定父级的代码:
Parent parent = entityManager.find(Parent.class, 1L);
entityManager.lock(parent, LockModeType.OPTIMISTIC_FORCE_INCREMENT);
// And use this parent to create the children as usual