JPA合并只读字段

时间:2010-07-12 12:03:24

标签: jpa java-ee jax-ws

我们有最简单的CRUD任务,包括JPA 1.0和JAX-WS 假设我们有一个实体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。

1 个答案:

答案 0 :(得分:1)

  

是功能还是仅仅是不一致?

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

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

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

  

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

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