为什么hibernate执行两个查询以急切加载@OneToOne双向关联?

我有实体A有一个B实体,而B有一个带有@OneToOne双向关联的A.

现在,当我找到所有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=? 

首先查询加载A和B字段,这没关系,但为什么要执行第二次查询来重新加载A? 我认为这个查询加载了B中的A内容,但是这个A显然是包含B的A …所以它已经加载了第一个查询,是不是真的?

– 编辑 –

实体A:

 @Entity public class A implements Serializable{ // id and other ecc ecc @OneToOne @JoinColumn(name="id_b") B b; } 

实体B:

 @Entity public class B implements Serializable{ // id and other ecc ecc @OneToOne(mappedBy="b") A a; } 

这就是情况,A上的findAll需要两个查询……为什么?

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; } } 

并且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; } } 

让我们测试(你可以测试你想要的)

 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 ba where b.id = :id") .setParameter("id", id) .list() .get(0); Assert.assertEquals(b.getId(), b.getA().getId()); 

注意我使用MutableInt字段(由Integer属性封装 )而不是Integer,因为Integer 是一个不可变类型,A和B共享SAME分配的id

但是如果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; } } 

和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; } } 

去测试

 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 ba where b.id = :id") .setParameter("id", id) .list() .get(0); 

通过使用所有者B, 您将获得两个select语句它是因为B Table 不包含指向表A的任何外键列但是通过使用

“来自A左边的连接获取ab,其中a.id =:id”

您将获得一个select语句,因为A知道如何使用其B_ID外键列检索其连接的B.

你的映射到底是什么样的?

您的AB类是否正确实现了hashCode()equals()以便Hibernate可以告诉B指向的A实例是第A的相同实例?

听起来您正在尝试建模双向一对一映射 – 请查看本手册中的部分,以查看完成它的推荐方法。