Java 如何防止Hibernate在PUT web请求中将列值更新为null?

Java 如何防止Hibernate在PUT web请求中将列值更新为null?,java,spring,hibernate,jpa,jackson,Java,Spring,Hibernate,Jpa,Jackson,我在我的spring boot应用程序上实现CRUD操作,我一直在实现PUT http请求来更新实体的某些值。特别是我的问题是,我的update方法会更新实体中的所有字段,而不管这些值是否为null。我在GitHub上查看了无数做同样事情的项目,但考虑到我的实体是复杂类型的,因为它们包含像@OneToMany/@OneToOne这样的关系,我无法找出最好的解决方案 这是我的实体类: @Entity @Table(name = "doctor") @JsonIdentityIn

我在我的spring boot应用程序上实现CRUD操作,我一直在实现PUT http请求来更新实体的某些值。特别是我的问题是,我的update方法会更新实体中的所有字段,而不管这些值是否为
null
。我在GitHub上查看了无数做同样事情的项目,但考虑到我的实体是复杂类型的,因为它们包含像@OneToMany/@OneToOne这样的关系,我无法找出最好的解决方案

这是我的实体类:

@Entity
@Table(name = "doctor")
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id", scope = Long.class)
@DynamicUpdate(value = true)
public class DoctorProfile implements Serializable {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @Column(length = 10)
  private String gender;

  @NotNull
  @Column(unique = true, name = "phone_number", length = 15)
  private String phoneNumber;

  @Column(name = "specialization", length = 15)
  private String specialization;

  @Column(name = "avatar", length = 200)
  private String avatar;

  @OneToOne(fetch = FetchType.LAZY, optional = false)
  @JoinColumn(name = "doctor_id", nullable = false)
  private Doctor doctor;

  @OneToOne(fetch = FetchType.LAZY, optional = false)
  @JoinColumn(name = "clinic_id", nullable = false)
  @JsonBackReference
  private Clinic clinic;

//Getter-Setters omitted
}
这是我的服务课:

  public ResponseEntity<DoctorProfile> updateDoctorProfile(DoctorProfile newInfo, long id){
    
    DoctorProfile dc = doctorProfileRepository.findById(id).orElseThrow(
            () -> new DoctorNotFoundException(id));

    dc.setGender(newInfo.getGender());
    dc.setPhoneNumber(newInfo.getPhoneNumber());
    dc.setSpecialization(newInfo.getSpecialization());
    dc.setClinic(newInfo.getClinic());
    dc.setAvatar(newInfo.getAvatar());

    final DoctorProfile updatedDoctor = doctorProfileRepository.save(dc);
    return ResponseEntity.ok(updatedDoctor);
  }
public ResponseEntity updateDoctorProfile(DoctorProfile新信息,长id){
DoctorProfile dc=doctorProfileRepository.findById(id).orelsetrow(
()->新DoctorNotFoundException(id));
setGender(newInfo.getGender());
dc.setPhoneNumber(newInfo.getPhoneNumber());
dc.setSpecialization(newInfo.getSpecialization());
setClinic(newInfo.getClinic());
setAvatar(newInfo.getAvatar());
最终DoctorProfile updatedDoctor=doctorProfileRepository.save(dc);
返回ResponseEntity.ok(updatedDoctor);
}
[
@RestController
省略类]

正如您所看到的,我尝试使用@DynamicUpdate注释,该注释应该保持
@RequestBody
null
的所有字段不变,但这对我不起作用

我还考虑实现一个逻辑,检查@RequestBody中的字段是否为null,并最终替换它们,从我试图更新的实体复制值。但这种解决方案有两个问题: •代码看起来很脏; •我无法通过
@RequestBody
更新@OneToOne等关系引用的值,比如我的
@Entity
中的Clinic或Doctor字段,因为我传递的不是整个类,而是ID,虽然这种方法在mysql上有效,但在Spring JPA上无效(我仍在试图找到解决方案)


我在这里读了一些关于堆栈溢出的答案,一些人建议创建一次只更新一个字段的方法,因为HTTP.PUT请求应该是这样工作的。但这真的是一个好方法吗?如果我有100200个甚至更多的列呢?对我来说似乎不切实际和重复。

你会有100-200列吗?有时可以逐个字段进行更新

此外,您还可以对信息进行分组,例如,
updatePersonalInformation
,并在其中放置多个字段,或者每个实体只更新一次,这样就可以更新DoctorProfile、Doctor、Clinic


另外,您是否考虑过使用单独的类进行更新,例如
DoctorProfileDto
,它只包含您真正想要更新的信息,这样,您就不会发现自己处于一种情况,即您认为自己有一些值,但它总是空的。

首先,PUT应该替换资源,因此,所有传递的状态都应该设置为与预期的语义匹配。您需要的是HTTP补丁

如果要使用它,必须首先加载实体,然后将JSON负载反序列化到该实体上。然后可以使用动态更新或不使用动态更新刷新该实体

我认为这是一个完美的用例,它提供了您想要的语义

我创建了这个库,以便在JPA模型和自定义接口或抽象类定义的模型之间进行简单的映射,类似于类固醇上的Spring数据投影。其思想是以您喜欢的方式定义目标结构(域模型),并通过JPQL表达式将属性(getter)映射到实体模型

在Blaze持久性实体视图中,您的用例的DTO模型可能如下所示:

@UpdatableEntityView
@EntityView(DoctorProfile.class)
public interface DoctorProfileDto {
    @IdMapping
    Long getId();
    String getGender();
    void setGender(String gender);
    String getPhoneNumber();
    void setPhoneNumber(String phoneNumber);
    String getSpecialization();
    void setSpecialization(String specialization);
    String getAvatar();
    void setAvatar(String avatar);
}
查询是将实体视图应用于查询的问题,最简单的就是按id进行查询

DoctorProfileDto a=entityViewManager.find(entityManager,DoctorProfileDto.class,id)

Spring数据集成允许您像使用Spring数据投影一样使用它:

下面是一个示例,说明了这可能是什么样子: