Search code examples
javajpaentity-relationshipopenjpa

Version of all children is incremented in OneToMany when merging parent entity


I have a OneToMany relationship defined like this:

@Entity
Parent extends BaseEntity {
    @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.MERGE})
    private List<Child> childList;
    // ...
}

@Entity
Child extends BaseEntity {
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "PARENT_ID")
    private Parent parent;
    // ...
}

The @Version annotation is defined in the BaseEntity class. The entities are converted to DTOs and changed by the client. Now when the client changes one of the child elements, the parent and its children are converted back to entities and a merge is done by executing em.merge(parent), the version of ALL children is incremented by one! I expected that the version of the changed child is incremented only. First I thought it is because of my EntityCallbackListener which is intercepting the merge with @PreUpdate. But if I comment out the callback method the version fields of the other children are still incremented. Does anyone have an explanation for this behaviour?

I'm using OpenJPA 1.2.3.


Okay, RTFM sometimes helps... :-/

Increasing the version on all children is OpenJPA default:

This lock manager does not perform any exclusive locking, but instead ensures read consistency by verifying that the version of all read-locked instances is unchanged at the end of the transaction. Furthermore, a write lock will force an increment to the version at the end of the transaction, even if the object is not otherwise modified. This ensures read consistency with non-blocking behavior.

This is the default openjpa.LockManager setting in JPA.

This setting can be overriden by using the pessimistic lock manager and its properties:

The pessimistic LockManager can be configued to additionally perform the version checking and incrementing behavior of the version lock manager described below by setting its VersionCheckOnReadLock and VersionUpdateOnWriteLock properties.

So I configured OpenJPA to not change the version on update:

<property name="openjpa.LockManager" value="pessimistic(VersionCheckOnReadLock=true,VersionUpdateOnWriteLock=false)"/>

But it does not work. The version fields of all children are still incremented. Do I miss something? What do I have to configure in order to have OpenJPA update the changed entities' version field only?


Solution

  • The problem was a incorrect equals() and hashcode() implementation so the EntityManager assumed all entities in the list had been changed.