【问题标题】:JPA merge readonly fieldsJPA 合并只读字段
【发布时间】:2011-03-14 18:52:38
【问题描述】:

我们使用 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 对象公开集合并在合并之前执行类似的操作。

Person existingPerson = entityManager.find(Person.class, person.getEmail());
person.setReadOnlyObject(existingPerson.getReadOnlyObject()); // Arghhh!

我的问题:

  • 它是一个功能还是只是 不一致?
  • 你怎么(或会 你)处理这种情况?请 不要建议我使用 DTO。

【问题讨论】:

  • 您有没有找到更好的替代方法来轻松使用不可更新的字段?

标签: jpa jakarta-ee jax-ws


【解决方案1】:

这是一个功能还是只是不一致?

我不知道,但我想说这是merge 的预期行为。以下是在实体上调用合并时发生的情况:

  • 现有实体被加载到持久化上下文中(如果还没有的话)
  • 从对象复制状态以合并到加载的实体
  • 对加载的实体所做的更改会在刷新时保存到数据库中
  • 加载的实体被返回

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

您(或者您会)如何处理这种情况?请不要建议我使用 DTO。

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

【讨论】:

  • “数据库中的空字段将被设置为空”如果我使用'updatable = false'则不是这种情况
  • 我在实体中有 40 多个字段,设置 40 并获得如此简单任务的调用并不好。
  • @Mykola 确实,如果您使用“updatable=false”,情况并非如此。但是,我的观点仍然是使用合并(并返回合并的实例)或“手动合并”。你目前的做法看起来很奇怪。
  • 拥有 40 套和获得更奇怪。只读视图有那么奇怪吗?无论如何,合并甚至不会存储在数据库中的字段的原因是什么(可更新 = false)
  • @Mykola 好吧,updatable=false 只是表示该字段不会成为 SQL 更新的一部分,仅此而已,我不会(也不会)期望对象级别的行为变化.换句话说,这并不意味着该字段是“只读”的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多