Spring 如何使用JpaRepository高效地更新

Spring 如何使用JpaRepository高效地更新,spring,postgresql,spring-data-jpa,sql-update,Spring,Postgresql,Spring Data Jpa,Sql Update,我有一个存储孩子列表的家长。当我更新子项(添加/编辑/删除)时,是否有方法根据外键自动决定删除或编辑哪个子项?或者我必须手动检查所有子项以查看哪些是新的或修改的 父类 @Entity @EntityListeners(PermitEntityListener.class) public class Permit extends Identifiable { @OneToMany(fetch = FetchType.LAZY, cascade=CascadeType.ALL, mappe

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

父类

@Entity
@EntityListeners(PermitEntityListener.class)
public class Permit extends Identifiable {

    @OneToMany(fetch = FetchType.LAZY, cascade=CascadeType.ALL, mappedBy = "permit")
    private List<Coordinate> coordinates;
}
家长控制器

@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);
}
@PutMapping(“”)
public@ResponseBody-ResponseEntity更新(@RequestBody-Permit-Permit){
debug(“id为{}的{}体的update(),permit,permit.getId());
如果(!repository.findById(permit.getId()).isPresent()){
返回ResponseEntity.status(HttpStatus.BAD_请求).body(null);
}
Permit returnedEntity=存储库.保存(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);
    }
@覆盖
@邮戳(“”)
public@ResponseBody ResponseEntity创建(@RequestBody-Permit-Permit){
debug(“create()和body{}”,permit);
if(permit==null | | permit.getId()!=null){
返回ResponseEntity.status(HttpStatus.BAD_请求).body(null);
}
列表坐标=permit.getCoordinates();
if(坐标!=null){
对于(int x=0;x
控制器更新

@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);
}
@PutMapping(“”)
public@ResponseBody-ResponseEntity更新(@RequestBody-Permit-Permit){
debug(“id为{}的{}体的update(),permit,permit.getId());
如果(!repository.findById(permit.getId()).isPresent()){
返回ResponseEntity.status(HttpStatus.BAD_请求).body(null);
}
List repocordinate=coordinatorepository.findByPermitId(permit.getId());
List coordinateds=new ArrayList();
对于(坐标:permit.getCoordinates()){
协调设置许可证(许可证);
//如果存在坐标,则将ID保存在CoordinateID中
if(coordinate.getId()!=null){
add(coordinate.getId());
}
}
//循环遍历存储库中的坐标,以查找要删除的坐标
用于(坐标:坐标){
if(!(coordinateIds.contains(coordinate.getId())){
CoordinatoreRepository.deleteById(coordinator.getId());
}
}
Permit returnedEntity=存储库.保存(Permit);
repository.flush();
return ResponseEntity.ok(returnedEntity);
}
我已经测试过了,它正在工作,没有简化的方法吗

我有一个存储孩子列表的家长

让我们为它编写DDL

TABLE parent (
  id integer pk
)
TABLE child(
  id integer pk
  parent_id integer FOREIGN KEY (parent.id)
)
当我更新子项(添加/编辑/删除)时,是否有方法根据外键自动决定删除或编辑哪个子项

假设您有一个新的孩子5与父母2绑定,并且:

  • DDL中的FK是正确的
  • 实体知道FK
  • 您使用的是相同的jpa上下文
  • 事务执行正确
  • 然后,每次调用
    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"
      }]
    }
    
    它将在表“许可”中创建一行,在表“坐标”中创建两行,两个坐标都绑定到许可行。结果将包括设置的ID

    但是:您必须进行验证工作(检查id是否为空,检查坐标是否未引用不同的许可证,…)

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

    DELETE /permit/972/coordinate/3826648305
    

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

    @Entity
    @EntityListeners(PermitEntityListener.class)
    public class Permit extends Identifiable {
    
        @OneToMany(mappedBy = "permit", cascade=CascadeType.ALL, orphanRemoval=true)
        private List<Coordinate> coordinates;
    
    }
    
    @实体
    @EntityListeners(permitityListener.class)
    公共类许可证{
    @OneToMany(mappedBy=“permit”,cascade=CascadeType.ALL,orphan=true)
    私人名单协调;
    }
    
    将映射标记为孤立删除将告诉基础ORM删除不再属于任何父实体的任何实体。由于从列表中删除了子元素,因此在保存父元素时将删除该子元素。 创建新元素和更新旧元素基于级联类型。因为您有CascadeType.com,所以在保存父实体时,列表中所有没有ID的元素都将保存到数据库并分配一个新ID,并且列表中所有已经有ID的元素都将更新

    另一方面,您可能需要更新列表坐标的setter方法,使其看起来像:

    public void setCoordinates(List<Coordinates> coordinates) {
        this.coordinates = coordinates;
        this.coordinates.forEach(coordinate -> coordinates.setPermit(this));
    }
    
    public void集合坐标(列表坐标){
    这个。坐标=坐标;
    this.coordinates.forEach(坐标->坐标.setPermit(this));
    }
    

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

    我想做的是更复杂的事情。。因为当用户修改记录时,他们可以添加新的子项、删除现有子项和编辑现有子项,
    public void setCoordinates(List<Coordinates> coordinates) {
        this.coordinates = coordinates;
        this.coordinates.forEach(coordinate -> coordinates.setPermit(this));
    }