【问题标题】:How to prevent Hibernate from updating column values as null in a PUT web requests?如何防止 Hibernate 在 PUT Web 请求中将列值更新为 null?
【发布时间】:2021-05-06 06:33:29
【问题描述】:

我正在我的 Spring Boot 应用程序上实施 CRUD 操作,但我一直坚持实施 PUT http 请求以仅更新我的实体的某些值。特别是我的问题是我的更新方法会更新我实体中的所有字段,无论这些值是否为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);
  }

[@RestController省略类]

如您所见,我尝试使用@DynamicUpdate 注释,它应该保持@RequestBody 中所有null 字段不变,但这对我不起作用。

我还考虑实现一个逻辑,检查@RequestBody 中的字段是否为空,并最终替换它们,从我尝试更新的实体中复制值。但是这个解决方案有两个问题: • 代码看起来很脏; •我不能真正更新由@OneToOne 到@RequestBody 等关系引用的值,比如我的@Entity 中的Clinic 或Doctor 字段,因为我没有通过整个班级,而只是通过了ID,而这方法适用于 mysql,但不适用于 Spring JPA(我仍在尝试找出解决方案)。

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

【问题讨论】:

    标签: java spring hibernate jpa jackson


    【解决方案1】:

    首先,PUT 应该替换一个资源,因此应该设置所有传递的状态以匹配预期的语义。你想要的是 HTTP PATCH。

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

    我认为这是 Blaze-Persistence Entity Views 的完美用例,它提供了您正在寻找的语义。

    我创建了该库以允许在 JPA 模型和自定义接口或抽象类定义模型之间轻松映射,例如 Spring Data Projections on steroids。这个想法是您按照自己喜欢的方式定义目标结构(域模型),并通过 JPQL 表达式将属性(getter)映射到实体模型。

    使用 Blaze-Persistence Entity-Views 的用例的 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 Data 集成让您可以像使用 Spring Data Projections 一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

    这是一个示例:https://github.com/Blazebit/blaze-persistence/blob/master/integration/spring-data/testsuite/webmvc/src/test/java/com/blazebit/persistence/spring/data/testsuite/webmvc/controller/DocumentController.java#L66

    【讨论】:

      【解决方案2】:

      你打算有 100-200 列吗?有时逐个字段更新是可以接受的。

      您还可以对信息进行分组,例如updatePersonalInformation 并在那里放置几个字段,或者每个实体只有 1 个更新,所以 DoctorProfile、Doctor、Clinic。

      另外,您是否考虑过使用单独的类进行更新,例如DoctorProfileDto,它只包含您真正想要更新的信息,这样您就可以避免陷入您认为自己有价值但它始终为空。

      【讨论】:

        猜你喜欢
        • 2011-11-22
        • 2021-01-24
        • 1970-01-01
        • 1970-01-01
        • 2013-02-08
        • 1970-01-01
        • 2013-04-11
        • 2017-01-31
        • 2019-08-04
        相关资源
        最近更新 更多