@Entity
public class A {
@GeneratedValue
@Id
private long id;
public long getId() {
return id;
}
public void setId(final long id) {
As stated in Nikos Paraskevopoulos' answer below, JPA and java objects require you to set both sides of a relationship. As long as you set the owning side, the database will be updated with the relationship changes, but the non-owning side will only reflect what is in the database if you manually set it, or you force it to be refreshed or reloaded. Reading the entity from a separate context does not force reloading, as your JPA provider can use a second level cache; this is the default in EclipseLink. Your other read is returning the A from the shared cache, which like your original object, does not have B added to its list of Bs.
The easiest solution is to just set B into A's list upfront. Other options here though would be to force the refresh of A using em.refresh(a) or with a query hint, or disable the shared cached.
There are 2 "facets" in the question: The Java side and the JPA side.
A more complete listing of the code might be:
@Entity
class A {
@OneToMany(mappedBy = "a")
@JoinTable
List<B> bs;
public List<B> getBs() {
return bs;
}
public void setBs(List<B> bs) {
this.bs = bs;
}
}
@Entity
class B {
@ManyToOne
@JoinTable
A a;
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
}
The JPA entites are still Java objects. If you do instruct Java explicitly to, e.g. "add the B in the collection of Bs, when its a property is set" it has no reason to do it automatically. Having said that, I have often seen patterns like (skipping null checking for brevity):
@Entity
class A {
...
public void addB(B b) {
bs.add(b);
b.setA(this);
}
public void removeB(B b) {
if( bs.remove(b) ) {
b.setA(null);
}
}
}
JPA 2.1. specs, ch. 2.9 "Entity Relationships":
A bidirectional relationship has both an owning side and an inverse (non-owning) side. A unidirectional relationship has only an owning side. The owning side of a relationship determines the updates to the relationship in the database, as described in section 3.2.4.
- The inverse side of a bidirectional relationship must refer to its owning side by use of the
mappedByelement
In the setup of the question, B.a is the owning side because A.bs specifies mappedBy="a". The specifications says that the relation will be updated (i.e. an entry in the join table will be inserted) only when the owning side is updated. That is why doing b.setA(a) updates the join table.
After doing the above and successfully updating the DB, reading the related A object fresh from the DB should fetch the correct bs collection. To be sure, first try merging the B, committing the transaction, and fetching A (or refreshing it) in a different transaction. If you want the state of the Java objects to be reflected immediately in the same transaction, you have no other option but to set both b.a and a.getBs().add(b).