Blow, if A and B share The same primary key column where both entities are joined by using their primary key, you should use @PrimaryKeyJoinColumn instead
@Entity
public class A implements Serializable {
private MutableInt id = new MutableInt();
private B b;
public void setIdAsMutableInt(MutableInt id) {
this.id = id;
}
@Id
@GeneratedValue
public Integer getId() {
return id.intValue();
}
public void setId(Integer id) {
this.id.setValue(id);
}
/**
* Any ToOne annotation, such as @OneToOne and @ManyToOne, is EARGELY loaded, by default
*/
@OneToOne(fetch=FetchType.LAZY)
@PrimaryKeyJoinColumn
@Cascade(CascadeType.SAVE_UPDATE)
public B getB() {
return b;
}
public void setB(B b) {
b.setIdAsMutableInt(id);
this.b = b;
}
}
And B Notice you do not need mappedBy attribute because of @PrimaryKeyJoinColumn
@Entity
public class B implements Serializable {
private MutableInt id = new MutableInt();
private A a;
public void setIdAsMutableInt(MutableInt id) {
this.id = id;
}
@Id
public Integer getId() {
return id.intValue();
}
public void setId(Integer id) {
this.id.setValue(id);
}
@OneToOne(fetch=FetchType.LAZY)
@PrimaryKeyJoinColumn
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
}
Let's Test (You can Test if you want)
A a = new A();
B b = new B();
a.setB(b);
/**
* b property will be saved because Cascade.SAVE_UPDATE
*/
Serializable id = session.save(a);
b = (B) session
.createQuery("from B b left join fetch b.a where b.id = :id")
.setParameter("id", id)
.list()
.get(0);
Assert.assertEquals(b.getId(), b.getA().getId());
Notice I use a MutableInt field (encapsulated by a Integer property) instead of Integer because Integer is a immutable Type as a way Both A and B share The SAME assigned id
But if A and B are joined by using other Than their primary key, you should use @JoinColumn and mappedBy (bi-directional relationship, right) as follows
@Entity
public class A implements Serializable {
private Integer id;
private B b;
@Id
@GeneratedValue
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
/**
* mappedBy="a" means: Look at "a" field / property at B Entity. If it has any assigned value, join us Through B_ID foreign key column
*/
@OneToOne(fetch=FetchType.LAZY, mappedBy="a")
/**
* Table A has a foreign key column called "B_ID"
*/
@JoinColumn(name="B_ID")
@Cascade(CascadeType.SAVE_UPDATE)
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
}
And B
@Entity
public class B implements Serializable {
private Integer id;
private A a;
public void setIdAsMutableInt(MutableInt id) {
this.id = id;
}
@Id
@GeneratedValue
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@OneToOne(fetch=FetchType.LAZY)
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
}
To test
A a = new A();
B b = new B();
/**
* Set up both sides
* Or use some kind of add convenience method
*/
a.setB(b);
b.setA(a);
/**
* b property will be saved because Cascade.SAVE_UPDATE
*/
Serializable id = session.save(a);
b = (B) session
.createQuery("from B b left join fetch b.a where b.id = :id")
.setParameter("id", id)
.list()
.get(0);
By using The owner side B, you will get Two select statements It occurs because B Table does not contain any foreign key column which points To Table A But by using
"from A a left join fetch a.b where a.id = :id"
You will get just one select statement because A knows how To retrieve its joined B by using its B_ID foreign key column