hibernate - 第二笔交易覆盖了第一笔交易的更改
问题描述
我在一个项目中工作,我们将 Spring Data Rest 与 HATEOAS、Spring JPA 和 Hibernate 一起使用,目前我们遇到了一个无法解决的奇怪问题:
- Angular 前端将两个 HTTP 请求传输到后端。两个请求都希望在同一行的不同列中添加关系(因为 HATEOAS 必须在两个请求中完成)。
- 创建了两个事务(A 和 B),Hibernate 在每个事务中执行选择以获取有问题的行。
- Hibernate 获取将在每个事务中链接的实体。
- Hibernate 在事务 A 中更新一次选定的行。事务 B 不久之后更新该行并将事务 A 中设置的值重置为事务开始时执行的初始选择的值。所以我们丢失了事务 A 的所有信息。
问题是这似乎不是确定性的。它有时会发生,然后在接下来的十次中正常工作。
我们无法通过添加锁定来解决这个问题(乐观锁定只会导致ObjectOptimisticLockingFailureException
事务 B),悲观锁定也没有效果。我们正在为实体使用 DynamicUpdate。我们尝试在交易上设置传播,但这并没有改变任何东西。现在我们没有想法,谷歌搜索没有产生任何结果。
应该更新的实体如下所示:
@AllArgsConstructor
@RequiredArgsConstructor
@NoArgsConstructor
@Data
@JsonIgnoreProperties(value = { "history" }, allowGetters = true)
@DynamicUpdate
@SelectBeforeUpdate
@Entity
@Table(name = "parameters")
public class Parameter
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "parameter_id", unique = true)
private Long id;
@NonNull
@Column(name = "name", nullable = false, unique = true)
private String key;
@ManyToOne(fetch = FetchType.EAGER,
cascade = {
CascadeType.REFRESH,
CascadeType.MERGE,
CascadeType.PERSIST,
CascadeType.DETACH
}
)
private Component component;
@OneToOne(fetch = FetchType.EAGER,
cascade = {
CascadeType.REFRESH,
CascadeType.MERGE,
CascadeType.PERSIST,
CascadeType.DETACH
}
)
private ParameterTypes type;
@OneToMany(
mappedBy = "parameter",
fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
orphanRemoval = true
)
@OrderBy("history_creation_timestamp DESC")
private List<History> history = new LinkedList<>();
@CreationTimestamp
@Column(name = "parameter_creation_timestamp")
private LocalDateTime creationTimestamp;
@UpdateTimestamp
@Column(name = "parameter_update_timestamp")
private LocalDateTime updateTimestamp;
}
component
并且字段type
应该更新。
如果我们能确保两个请求都正确更新行,或者事务 B 等待事务 A 完成并重新选择处于更新状态的行,那就太好了。
我们非常感谢任何帮助,因为我们在过去三天内无法找到可行的解决方案。我们很乐意提供任何其他信息。谢谢大家。
前端
我们使用angular4-hal和 Angular 7 与 Spring 后端进行通信。该调用相当简单,如下所示:
saveParameter(): void {
this.editMode = false;
const saveObservs: Observable<any>[] = [];
if (this.componentControl.dirty) {
saveObservs.push(this.parameter.addRelation('component', this.componentControl.value));
}
if (this.typeControl.dirty) {
saveObservs.push(this.parameter.addRelation('type', this.typeControl.value));
}
forkJoin(
saveObservs
).pipe(
catchError(err => of(err))
).subscribe(val => console.log(val));
}
componentControl
并且typeControl
都是FormControl
实例。
该addRelation
函数如下所示:
Resource.prototype.addRelation = function (relation, resource) {
if (this.existRelationLink(relation)) {
var header = ResourceHelper.headers.append('Content-Type', 'text/uri-list');
return ResourceHelper.getHttp().put(ResourceHelper.getProxy(this.getRelationLinkHref(relation)), resource._links.self.href, { headers: header });
}
else {
return observableThrowError('no relation found');
}
};
函数的上下文可以在这里找到
解决方案
不幸的是,我不了解 Angular 7,但我有一种saveObservs.push
异步工作的感觉。这意味着有时第二个请求在第一个请求完成之前到达 API ,从数据库加载未修改的记录,然后只修改第二个引用将其写回。
所以第二次推送应该等待第一次推送的结果。
但是,如果您尝试同时从两个客户端修改同一个对象,则无法解决此问题。
您应该version
向实体添加属性,如果您尝试使用过时版本修改对象,Spring Data Rest 将自动响应错误。
@Version
private Long version;
推荐阅读
- python - 在 tkinter 中管理用户输入小部件
- html - 发送文件返回空白视频
- c++ - QtConcurrent::mapped 返回类型问题
- css - 字体真棒图标未显示 css 内容属性
- c# - 在不使用循环的情况下计算 List 中的时间
- c++ - LAB_3.exe 中 0x00898809 处的未处理异常:0xC0000005:访问冲突读取位置 0xCCCCCCF4。发生了
- ruby - 如何使 Ruby YAML.load 以我想要的方式解析时间?
- postgresql - 配置 postgres 复制时服务器的启动备份映像
- mql4 - 如何使用 OrderModify() 修改所有当前未平仓头寸的止盈?
- javascript - 如何通过文本而不是值设置 select2 选定值?