
Question:
i have entity A that has-a B entity, and B has-a A with @OneToOne bidirectional association.
Now, when i findall A records, hibernate perform two queries with a left outer join on B, something like this:
select a.id, a.id_b, a.field1, b.id, b.field1 from A as a, B as b left outer join b ON b.id=a.id_b; select a.id, a.id_b, a.field1, b.id, b.field1 from A as a, B as b left outer join b ON b.id=a.id_b WHERE b.id=?
First query load A and B fields and it is ok, but why perform second query to reload A? I think this query load the A content in B, but this A is obviusly the A that contains B... so its already loaded with first query, isn't true?
-- EDIT --
Entity A:
@Entity public class A implements Serializable{ // id and other ecc ecc @OneToOne @JoinColumn(name="id_b") B b; }
Entity B:
@Entity public class B implements Serializable{ // id and other ecc ecc @OneToOne(mappedBy="b") A a; }
This is the situation, and a findAll on A need two queries... why?
Solution:1
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
Solution:2
What does your mapping look like exactly?
Do your A
and B
classes correctly implement hashCode()
and equals()
so that Hibernate can tell that the A
instance pointed to by B
is the same instance of the first A
?
Sounds like you are trying to model a bi-directional one-to-one mapping - take a look at the section in the manual on this to see the recommended methods for accomplishing it.
Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
EmoticonEmoticon