我有一个实体A,它具有-B实体,而B具有-A与@OneToOne双向关联。
现在,当我找到所有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需要两个查询…为什么?
如果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, 您不需要 mapedBy 属性 *
@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 b.a 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和maptedBy(双向关系,右),如下所示
@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 b.a where b.id = :id") .setParameter("id", id) .list() .get(0);
通过使用所有者方B, 您将获得两个select语句。 这是因为B表 不包含任何指向表A的外键列, 但是通过使用
“从A左连接获取ab,其中a.id =:id”
您将 仅 获得 一条select语句, 因为A知道如何使用其B_ID外键列来检索其联接的B。