JPA merge read-only fields
We have the simplest crud tasks, including JPA 1.0 and jax-ws
@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 }
This is the scene The client creates a web service request to create a person On the server side, everything is simple It did work as expected
@Stateless @WebService public class PersonService { @PersistenceContext(name = "unit-name") private EntityManager entityManager; public Person create(Person person) { entityManager.persist(person); return person; } }
Now the client tries to update the staff, which is where I, JPA, show its inconsistency
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; }
To avoid this problem, I need to expose the set for the readonly object and perform similar operations before merging
Person existingPerson = entityManager.find(Person.class,person.getEmail()); person.setReadOnlyObject(existingPerson.getReadOnlyObject()); // Arghhh!
My question:
>Is this a function or just inconsistent? > how are you? You) deal with this situation? Please do not suggest that I use dto
Solution
I don't know, but I would say this is the expected behavior of the merger The following is what happens when you call merge on an entity:
>Existing entities load in persistence context (if not already present) > copy state from object to merge into loaded entity > changes made to loaded entity will be saved to database on refresh > return loaded entity
This applies to simple cases, but if you receive a partial value object (some fields or associations are set to null) to merge, it will not work properly: the empty fields in the database will be set to null, which may not be what you want
In this case, you should use "manual merge": use find to load existing entities and update the fields to be updated by copying the new state, so that JPA can detect the changes and refresh them to the database