如何使用JpaRepository高效更新



我有一个存储子级列表的父级。当我更新子项(添加/编辑/删除)时,是否有一种方法可以根据外键自动决定删除或编辑哪个子项?还是我必须手动检查所有的孩子,看看哪些是新的或修改过的?

父类

@Entity
@EntityListeners(PermitEntityListener.class)
public class Permit extends Identifiable {
@OneToMany(fetch = FetchType.LAZY, cascade=CascadeType.ALL, mappedBy = "permit")
private List<Coordinate> coordinates;
}

儿童类

@Entity
public class Coordinate extends Identifiable {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "permit_id", referencedColumnName = "id")
private Permit permit;
private double lat;
private double lon;
}

家长控制器

@PutMapping("")
public @ResponseBody ResponseEntity<?> update(@RequestBody Permit permit) {
logger.debug("update() with body {} of id {}", permit, permit.getId());
if (!repository.findById(permit.getId()).isPresent()) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}
Permit returnedEntity = repository.save(permit);
repository.flush();
return ResponseEntity.ok(returnedEntity);
}

=编辑=

控制器创建

@Override
@PostMapping("")
public @ResponseBody ResponseEntity<?> create(@RequestBody Permit permit) {
logger.debug("create() with body {}", permit);
if (permit == null || permit.getId() != null) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}
List<Coordinate> coordinates = permit.getCoordinates();
if (coordinates != null) {
for (int x = 0; x < coordinates.size(); ++x) {
Coordinate coordinate = coordinates.get(x);
coordinate.setPermit(permit);
}
}
Permit returnedEntity = repository.save(permit);
repository.flush();
return ResponseEntity.ok(returnedEntity);
}

控制器更新

@PutMapping("")
public @ResponseBody ResponseEntity<?> update(@RequestBody Permit permit) {
logger.debug("update() with body {} of id {}", permit, permit.getId());
if (!repository.findById(permit.getId()).isPresent()) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}
List<Coordinate> repoCoordinate = coordinateRepository.findByPermitId(permit.getId());
List<Long> coordinateIds = new ArrayList<Long>();
for (Coordinate coordinate : permit.getCoordinates()) {
coordinate.setPermit(permit);
//if existing coordinate, save the ID in coordinateIds
if (coordinate.getId() != null) {
coordinateIds.add(coordinate.getId());
}
}
//loop through coordinate in repository to find which coordinate to remove
for (Coordinate coordinate : repoCoordinate) {
if (!(coordinateIds.contains(coordinate.getId()))) {
coordinateRepository.deleteById(coordinate.getId());
}
}
Permit returnedEntity = repository.save(permit);
repository.flush();
return ResponseEntity.ok(returnedEntity);
}

我已经测试过了,它正在发挥作用,难道没有简化的方法吗?

您已接近解决方案。你唯一缺少的是orphanRemoval=true在你的一对多映射上:

@Entity
@EntityListeners(PermitEntityListener.class)
public class Permit extends Identifiable {
@OneToMany(mappedBy = "permit", cascade=CascadeType.ALL, orphanRemoval=true)
private List<Coordinate> coordinates;
}

将映射标记为孤立删除将告诉底层ORM删除不再属于任何父实体的任何实体。由于从列表中删除了子元素,因此在保存父元素时,该子元素将被删除。创建新元素和更新旧元素是基于CascadeType的。因为您有CascadeType。保存父实体时,列表中所有没有ID的元素都将保存到数据库中,并分配一个新的ID,并且列表中已存在且具有ID的所有元素都将更新。

附带说明一下,您可能需要更新List坐标的setter方法,使其看起来像:

public void setCoordinates(List<Coordinates> coordinates) {
this.coordinates = coordinates;
this.coordinates.forEach(coordinate -> coordinates.setPermit(this));
}

或者,如果您使用JSON,只需使用@JsonManagedReference和@JsonBackReference。

我有一个存储子级列表的父级。

让我们为它编写DDL。

TABLE parent (
id integer pk
)
TABLE child(
id integer pk
parent_id integer FOREIGN KEY (parent.id)
)

当我更新子项(添加/编辑/删除)时,是否有一种方法可以根据外键自动决定删除或编辑哪个子项?

假设您有一个新的子#5绑定到父#2,并且:

  1. DDL中的FK正确
  2. 实体知道FK
  3. 您使用的是相同的jpa上下文
  4. 交易执行正确

然后,对parent.getChilds()的每次调用都必须(!)返回事务执行前存在的所有实体您刚刚提交到数据库的实体的同一实例。

然后,如果删除父级#2的子级#5,并且事务成功执行,则parent.getChilds()必须返回所有不包含子级#5的实体。

特殊情况:

如果删除了父级#2,并且在DDL和Java代码中进行了级联删除,则必须从数据库中删除所有子级以及刚刚删除的数据库中的父级#2。在这种情况下,父#2不再绑定到jpa上下文,并且父#2的所有子级不再绑定到jpa上下文。

=编辑=

您可以使用merge。这将适用于以下结构:

POST {
"coordinates": [{
"lat":"51.33",
"lon":"22.44"
},{
"lat":"50.22",
"lon":"22.33"
}]
}

它将在表"permit"中创建一行,在表"coordinate"中创建两行,这两个坐标都绑定到permit行。结果将包括ID集。

但是:你必须做验证工作(检查id是否为null,检查坐标是否引用不同的许可证,…)!

必须使用DELETE方法删除坐标:

DELETE /permit/972/coordinate/3826648305

最新更新