JPA 合并只读字段

标签 jpa jakarta-ee jax-ws

我们有使用 JPA 1.0 和 JAX-WS 的最简单的 CRUD 任务。
假设我们有一个实体 Person。

@Entity
public class Person
{
   @Id
   private String email;

   @OneToOne(fetch = FetchType.LAZY)
   @JoinColumn(insertable = false, updatable = false)
   private ReadOnly readOnly;

   @Column
   private String name;      

   @XmlElement
   public String getEmail()
   {
      return email;
   }

   public void setEmail(String email)
   {
      this.email = email;
   }

   @XmlElement
   public Long getReadOnlyValue()
   {
      return readOnly.getValue();
   }

   // more get and set methods
}

这是场景。
客户端发出 Web 服务请求以创建人员。在服务器端,一切都很简单。
它确实按预期工作。
@Stateless
@WebService
public class PersonService
{
   @PersistenceContext(name = "unit-name")
   private EntityManager entityManager;

   public Person create(Person person)
   {
      entityManager.persist(person);

      return person;
   }
}

现在客户端尝试更新人员,这就是对我而言,JPA 显示其不一致的地方。
public Person update(Person person)
{
   Person existingPerson = entityManager.find(Person.class, person.getEmail());

   // some logic with existingPerson
   // ...      

   // At this point existingPerson.readOnly is not null and it can't be null
   // due to the database.
   // The field is not updatable.
   // Person object has readOnly field equal to null as it was not passed 
   // via SOAP request.
   // And now we do merge.

   entityManager.merge(person);

   // At this point existingPerson.getReadOnlyValue() 
   // will throw NullPointerException. 
   // And it throws during marshalling.
   // It is because now existingPerson.readOnly == person.readOnly and thus null.
   // But it won't affect database anyhow because of (updatable = false)

   return existingPerson;
}

为了避免这个问题,我需要为 readOnly 对象公开 set 并在合并之前做这样的事情。
Person existingPerson = entityManager.find(Person.class, person.getEmail());
person.setReadOnlyObject(existingPerson.getReadOnlyObject()); // Arghhh!

我的问题:
  • 它是一个功能还是只是
    不一致?
  • 你如何(或会
    你)处理这种情况?请
    不要建议我使用 DTO。
  • 最佳答案

    Is it a feature or just inconsistence?



    我不知道,但我想说这是 merge 的预期行为.以下是对实体调用合并时发生的情况:
  • 现有实体被加载到持久化上下文中(如果还没有)
  • 状态从对象复制到合并到加载的实体
  • 对加载的实体所做的更改在刷新时保存到数据库
  • 加载的实体返回

  • 这适用于简单的情况,但如果您收到一个部分值的对象(某些字段或关联设置为 null )到 merge 则不行。 : 数据库中的空字段将设置为空,这可能不是您想要的。

    How do you (or would you) handle such situations? Please don't advice me to use DTOs.



    在这种情况下,您应该使用“手动合并”:使用 find 加载现有实体并通过复制新状态来更新自己要更新的字段,让 JPA 检测更改并将它们刷新到数据库中。

    关于JPA 合并只读字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3228304/

    相关文章:

    java - Spring Data REST - @PrePersist 和@HandleBeforeCreate 之间的区别?

    java - 过滤所有查询的数据

    jakarta-ee - "Unable to retrieve Drone Instance within 60 seconds"

    mysql - Spring jpa hibernate mysql LocalDate 在坚持一天后关闭

    java - 用于处理单页 Web 应用程序设计的单个 servlet

    java - 如何在jsp页面中调用web服务

    java - JAX-WS wsimport 不会保留 toString()?

    jax-ws - 删除 JAX-WS SOAP 响应中的元素 <return>

    java - 使用 JAX-WS 的 SOAP 消息中的嵌套标记中没有命名空间

    java - 如何在 EJB JPA 中合并两个日期