jpa - Spring Batch 在读取之前处理了 JpaPagingItemReader enityManager.flush()
问题描述
我有一个标准的双向父子关系如下:
@Entity
public class Parent {
@Id
private Long id;
private String field1;
private String field2;
@OneToMany(
mappedBy = "parent",
orphanRemoval = true,
cascade = CascadeType.ALL
)
private Set<Child> children = new HashSet<>();
// remain code omitted
}
@Entity
public class Child {
@Id
private Long id;
@ManyToOne(
fetch = FetchType.LAZY
)
private Parent parent;
// remain code omitted
}
和一个 jpapagegitemreader,它读取具有子实体的父实体,项目处理器更新一些父字段并孤立子实体。
public class ParentItemProcessor implements ItemProcessor<Parent, Parent> {
@Override
public Parent process(Parent parent) throws Exception {
parent.setField1("new val");
parent.setField2("new val");
parent.getChildren().clear();
return parent;
}
}
这一切正常,我可以看到何时提交块,为父更新和子删除执行 sql。
我遇到的问题是在提交第一个块之后,阅读器在执行读取下一批的查询之前执行以下代码。对“entityManager.flush()”的调用会导致重新执行删除语句,从而导致异常:
引起:org.hibernate.StaleStateException:批量更新从更新[0]返回了意外的行数;实际行数:0;预期:1;执行的语句:从 child 中删除 child_id=?
protected void doReadPage() {
EntityTransaction tx = null;
if (transacted) {
tx = entityManager.getTransaction();
tx.begin();
entityManager.flush();
entityManager.clear();
}
// truncated
}
显然,我可以通过在阅读器上将 'transacted' 设置为 false 来绕过此代码(这可行),但我不确定这意味着什么以及 spring 批处理的最佳实践,因为 'transacted' 是默认行为。
我还想了解在读取新项目之前在阅读器中调用 flush 的需要,特别是当阅读器和编写器具有不同的实体管理器时。
谢谢
解决方案
推荐阅读
- sql-server - 什么会导致 MS SQL Server 重新创建解释计划?
- php - 产品类别过滤器显示产品列表及其在 Woocommerce 中的库存
- html - 文本按钮前的图像
- javascript - 如何检测元素本身触发的事件?
- python - 从没有 sys.path 的父级的 Python 相对导入
- javascript - 如何使用 fetch() 和 WhatWG 流获取文件上传进度
- xcode - Xcode 10 无法完全显示子类的名称
- c# - 如何申请 Not In Linq?
- c++ - std::random_device 是否限制 g++ 上的实例数量?
- sql-server - 仅在最高值上的 SQL 连接表