为什么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.
你的映射到底是什么样的?
您的A
和B
类是否正确实现了hashCode()
和equals()
以便Hibernate可以告诉B
指向的A
实例是第A
的相同实例?
听起来您正在尝试建模双向一对一映射 – 请查看本手册中的部分,以查看完成它的推荐方法。