|
Exploring the possibilities on optimistic locking while tracking changes on a OneToMany-relation we tried to setup the relation in a way where the parent is "owner". This does not seem to be very common nor well documented, so we may be led astray here - it however seems to be supported to some extend:
The easy case is that the relation is unidirectional from parent to child
@OneToMany(cascade=CascadeType.ALL, orphanRemoval= true)
@JoinColumn(name = "parent_id")
public Set<UnidirectionalChild> getChildren()
with no corresponding relation on the child side the parent is clearly the owner. The parent version is perfectly updated in this case when children are added or removed.
A more complex case is when we add the relation on the child side making it bidirectional - the "optional" case first:
@ManyToOne(optional = true)
public BidirectionalOptionalParent getParent()
Please note, that there is (still) no mappedBy at the OneToMany annotation. If there would be, the child would be the owner of the relation and we would lose the optimistic locking on collection changes.
This bidirectional-optional scenario works as expected as well. However while removing a child Hibernate issues an extra update on that child to set the parent to null right before deleting it:
update BidirectionalOptionalChild set parent_id=null where parent_id=? and id=?
delete from BidirectionalOptionalChild where id=? and version=?
If you now make the child-to-parent-relation mandatory
@ManyToOne(optional = false)
public BidirectionalMandatoryParent getParent()
removing a child is no longer possible due to the extra update mentioned above which leads to a "NULL not allowed" exception.
To complete the cycle we finally found a way to handle the mandatory case as well using (abusing?) the @OptimisticLock annotation:
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "parent")
@OptimisticLock(excluded = false)
public Set<BidirectionalOptimisticLockChild> getChildren()
on the parent side and
@ManyToOne(optional = false)
@JoinColumn(name = "parent_id")
public BidirectionalOptimisticLockParent getParent()
at the child side. This bidirectional-mandatory-optimistic-lock scenario works as expected now.
You may find all cases in a test project on Github https://github.com/abenneke/sandbox/tree/master/hibernate-versions as {{*ParentOwnedTest}}s.
Thank you!
|