[英]Why hibernate perform two queries for eager load a @OneToOne bidirectional association?
i have entity A that has-a B entity, and B has-a A with @OneToOne bidirectional association. 我有实体A有一个B实体,而B有一个带有@OneToOne双向关联的A.
Now, when i findall A records, hibernate perform two queries with a left outer join on B, something like this: 现在,当我找到所有A记录时,hibernate在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;
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? 首先查询加载A和B字段,这没关系,但为什么要执行第二次查询来重新加载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? 我认为这个查询加载了B中的A内容,但是这个A显然是包含B的A ......所以它已经加载了第一个查询,是不是真的?
-- EDIT -- - 编辑 -
Entity A: 实体A:
@Entity
public class A implements Serializable{
// id and other ecc ecc
@OneToOne
@JoinColumn(name="id_b")
B b;
}
Entity B: 实体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? 这就是情况,A上的findAll需要两个查询......为什么?
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 Blow,如果A和B 共享使用主键 连接两个实体的同一主键列 ,则应使用@PrimaryKeyJoinColumn
@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 并且B请注意,由于@PrimaryKeyJoinColumn, 您不需要mappedBy属性
@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 注意我使用MutableInt字段(由Integer属性封装 )而不是Integer,因为Integer 是一个不可变类型,A和B共享SAME分配的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 但是如果A和B 通过使用其他主键而加入 ,则应使用@JoinColumn和mappedBy(双向关系,右),如下所示
@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 而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 通过使用所有者B, 您将获得两个select语句它是因为B Table 不包含指向表A的任何外键列但是通过使用
"from A a left join fetch ab where a.id = :id" “来自A左连接获取ab,其中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 您将只获得一个select语句,因为A知道如何使用其B_ID外键列检索其连接的B.
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
? 您的A
和B
类是否正确实现了hashCode()
和equals()
以便Hibernate可以告诉B
指向的A
实例是第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. 听起来您正在尝试建模双向一对一映射 - 请查看本手册中的部分,以查看完成它的推荐方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.