We would like to use the association management functionality of Hibernate (5.4.2.Final), together with bytecode enhancement and field access. This works for some cases and not for others beyond our understanding - would appreciate help/guidance.
We've distilled the case as shown below but (legacy, perhaps unnecessary) annotations are preserved.
In the simplest case there are two entity types, a "Foo" and a "Link". "Link" is a simple directed relationship between two Foo entities. Foo has two collection properties that reflect this relationship - incoming and outgoing. We start with the database having just a single instance of Foo with an id of 1. We then try to, in a single transaction, create another (fresh) Foo (id=2) and a Link from Foo#1 (loaded) to Foo#2 (fresh). We set the Link's "from" and "to" properties and desire/expect the bytecode enhancement / association management to update the "other sides" of those relations. What happens is that the freshLink.setTo() is reflected in the freshFoo.incoming property but the freshLink.setFrom() is NOT reflected in the loadedFoo.outgoing property.
Can anyone help us figure out why? What are we doing wrong or expecting what we should not?
Main code is:
final Foo loadedFoo = session.get(Foo.class, 1L);
final Foo freshFoo = new Foo();
freshFoo.setId(2L);
final Link freshLink = new Link();
freshLink.setId(1L);
freshLink.setTo(freshFoo);
System.out.println("Other side of 'to' (in freshFoo) updated: " + freshFoo.getIncoming().contains(freshLink));
freshLink.setFrom(loadedFoo);
System.out.println("Other side of 'from' (in loadedFoo) updated: " + loadedFoo.getOutgoing().contains(freshLink) );
session.save(freshFoo);
session.save(freshLink);
session.saveOrUpdate(loadedFoo);
Foo code is:
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@javax.persistence.Entity
@Table(name = "foo")
@org.hibernate.annotations.BatchSize(size = 10)
@org.hibernate.annotations.DynamicInsert(false)
@org.hibernate.annotations.DynamicUpdate(false)
@org.hibernate.annotations.SelectBeforeUpdate(false)
@org.hibernate.annotations.Proxy(lazy = false)
@org.hibernate.annotations.Polymorphism(type = org.hibernate.annotations.PolymorphismType.IMPLICIT)
@org.hibernate.annotations.Cache(include = "all", usage = org.hibernate.annotations.CacheConcurrencyStrategy.READ_WRITE)
@javax.persistence.Access(javax.persistence.AccessType.FIELD)
public class Foo {
@Id
@Column(name = "id", nullable = false, updatable = false)
@org.hibernate.annotations.Type(type = "long")
private Long id = null;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "to")
@org.hibernate.annotations.LazyCollection(org.hibernate.annotations.LazyCollectionOption.EXTRA)
@org.hibernate.annotations.OptimisticLock(excluded = false)
@org.hibernate.annotations.Fetch(org.hibernate.annotations.FetchMode.SUBSELECT)
@org.hibernate.annotations.NotFound(action = org.hibernate.annotations.NotFoundAction.EXCEPTION)
private Set<Link> incoming = new HashSet<>();
@OneToMany(fetch = FetchType.LAZY, mappedBy = "from")
@org.hibernate.annotations.LazyCollection(org.hibernate.annotations.LazyCollectionOption.EXTRA)
@org.hibernate.annotations.OptimisticLock(excluded = false)
@org.hibernate.annotations.Fetch(org.hibernate.annotations.FetchMode.SUBSELECT)
@org.hibernate.annotations.NotFound(action = org.hibernate.annotations.NotFoundAction.EXCEPTION)
private Set<Link> outgoing = new HashSet<>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Set<Link> getIncoming() {
return incoming;
}
public Set<Link> getOutgoing() {
return outgoing;
}
public void addIncoming(Link link) {
this.incoming.add(link);
}
public void addOutgoing(Link link) {
this.outgoing.add(link);
}
}
Link code is:
import javax.persistence.Column;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@javax.persistence.Entity
@Table(name = "link")
@org.hibernate.annotations.BatchSize(size = 100)
@org.hibernate.annotations.DynamicInsert(false)
@org.hibernate.annotations.DynamicUpdate(false)
@org.hibernate.annotations.SelectBeforeUpdate(false)
@org.hibernate.annotations.Proxy(lazy = false)
@org.hibernate.annotations.Polymorphism(type = org.hibernate.annotations.PolymorphismType.IMPLICIT)
@org.hibernate.annotations.Cache(include = "all", usage = org.hibernate.annotations.CacheConcurrencyStrategy.READ_WRITE)
@javax.persistence.Access(javax.persistence.AccessType.FIELD)
public class Link {
@Id
@Column(name = "id", nullable = false, updatable = false)
@org.hibernate.annotations.Type(type = "long")
private Long id = null;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "from_id", nullable = false)
@org.hibernate.annotations.LazyToOne(org.hibernate.annotations.LazyToOneOption.NO_PROXY)
@org.hibernate.annotations.OptimisticLock(excluded = false)
@org.hibernate.annotations.Fetch(org.hibernate.annotations.FetchMode.SELECT)
@org.hibernate.annotations.NotFound(action = org.hibernate.annotations.NotFoundAction.EXCEPTION)
private Foo from;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "to_id", nullable = false)
@org.hibernate.annotations.LazyToOne(org.hibernate.annotations.LazyToOneOption.NO_PROXY)
@org.hibernate.annotations.OptimisticLock(excluded = false)
@org.hibernate.annotations.Fetch(org.hibernate.annotations.FetchMode.SELECT)
@org.hibernate.annotations.NotFound(action = org.hibernate.annotations.NotFoundAction.EXCEPTION)
private Foo to;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Foo getFrom() {
return from;
}
public void setFrom(Foo from) {
this.from = from;
}
public Foo getTo() {
return to;
}
public void setTo(Foo to) {
this.to = to;
}
}