spring - 如何在 Spring JPA 中防止并发实体创建
问题描述
我有以下问题
我的服务是 Rest 控制器和 JPA Repository 之间的链接,在数据库中创建实体之前会执行一些检查。但是出现了下面的问题,如果客户端 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;
解决方案
在 parent 上启用乐观锁定,并 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
推荐阅读
- android - 将 cocos2d Android 应用程序导出为 aar 并从另一个项目调用其 Activity
- csv - 调整 CSV 形状(所有值都在最后一列下)
- excel - 计算cp & cpk
- java - 如果没有 JUnit 的 @Test 注解,Spock 测试用例将无法工作
- git - 如何将 git log -L /regex/ 与换行符一起使用?
- microservices - 在 Cosmos DB 中嵌入来自另一个文档的数据
- regex - 如何使用 2 个正则表达式和一个返回值?
- python - Python按另一个列表排序列表,但排序的是日期类型
- firebase - fireauth 检测用户创建
- apache - 我的 Apache conf 是否在永无止境的循环中,我该如何改进?