如果定义了双向关系,Hibernate将执行两次相同的查询 [英] Hibernate performs twice the same query if a bidirectional relationship is defined

查看:39
本文介绍了如果定义了双向关系,Hibernate将执行两次相同的查询的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Hibernate 4.3.8.Final和Oracle 11g数据库.我在两个名为Parent和Child的实体之间定义了一个非常简单的双向关系,如下所示(省略了getter和setter):

I'm using Hibernate 4.3.8.Final and Oracle 11g database. I defined a very simple bidirectional relationship between two entities, named Parent and Child, as follows (getters and setters are omitted):

@Entity
public class Parent {

    @Id
    private Long id;

    @OneToOne(mappedBy="parent",fetch=FetchType.LAZY)
    private Child child;
}

@Entity
public class Child {

    @Id
    private Long id;

    @OneToOne(fetch=FetchType.EAGER)
    @JoinColumn(name="PARENT_ID")
    private Parent parent;
}

生成相应表的SQL代码为:

The SQL code which generates the corresponding tables is:

CREATE TABLE PARENT
(
  ID  NUMBER(10)
);
CREATE UNIQUE INDEX PARENT_PK ON PARENT(ID);
ALTER TABLE PARENT ADD (
  CONSTRAINT PARENT_PK
  PRIMARY KEY
  (ID)
  USING INDEX PARENT_PK
  ENABLE VALIDATE);

--------------

CREATE TABLE CHILD
(
  ID         NUMBER(10),
  PARENT_ID  NUMBER(10)                         NOT NULL
);

CREATE UNIQUE INDEX CHILD_PK ON CHILD (ID);

CREATE UNIQUE INDEX CHILD_U01 ON CHILD (PARENT_ID);

ALTER TABLE CHILD ADD (
  CONSTRAINT CHILD_PK
  PRIMARY KEY
  (ID)
  USING INDEX CHILD_PK
  ENABLE VALIDATE,
  CONSTRAINT CHILD_U01
  UNIQUE (PARENT_ID)
  USING INDEX CHILD_U01
  ENABLE VALIDATE);

ALTER TABLE CHILD ADD (
  CONSTRAINT CHILD_R01 
  FOREIGN KEY (PARENT_ID) 
  REFERENCES PARENT (ID)
  ENABLE VALIDATE);

结构非常简单:子项通过唯一的外键( PARENT_ID )链接到父项.使用以下代码从数据库中检索子实例:

The structure is very simple: the child is linked to the parent by a foreign key (PARENT_ID) which is also unique. A child instance is retrieved from the database using the code below:

entityManager.find(Child.class,1l);

Hibernate执行两个查询.似乎第一个用于加载关系的第一个方向(从子级到父级),第二个用于加载另一个关系(从父级到子级):

Hibernate performs two queries. It seems that the first one is used to load the first direction of the relationship (from child to parent) and the second one is used to load the other relationship (from parent to child):

SELECT child0_.id AS id1_0_0_,
       child0_.PARENT_ID AS PARENT_ID2_0_0_,
       parent1_.id AS id1_1_1_
  FROM    Child child0_
       LEFT OUTER JOIN
          Parent parent1_
       ON child0_.PARENT_ID = parent1_.id
 WHERE child0_.id = ?;

SELECT child0_.id AS id1_0_1_,
       child0_.PARENT_ID AS PARENT_ID2_0_1_,
       parent1_.id AS id1_1_0_
  FROM    Child child0_
       LEFT OUTER JOIN
          Parent parent1_
       ON child0_.PARENT_ID = parent1_.id
 WHERE child0_.PARENT_ID = ?;

Child被配置为热切加载Parent,因此第一个查询是正确的:Child和Parent表已连接并被获取.我试图将父级"的子级"属性的获取策略设置为LAZY,以防止进行第二次查询,但这没有效果.

The Child is configured to eagerly load the Parent, so the first query is correct: the Child and Parent tables are joined and fetched. I tried to set the fetch policy of the "child" property of the Parent to LAZY in order to prevent the second query, but it has no effect.

如何使用单个查询快速加载双向关系?实际上,如果检索到N个Child实例,则将执行N + 1个查询.

How can I load eagerly a bidirectional relationship using a single query? In fact, if N Child instances are retrieved then N+1 queries are executed.

推荐答案

此处所指出的,解决方案是使用无所有权的一面.如果您修改映射,则可以通过以下方式将拥有方移到父级"(为简洁起见保护字段包):

As pointed out here, the solution is to use the non-owning side. If you modify the mapping moving the owning side to Parent (fields package protected for brevity) by:

@Entity
public class Parent {
    @Id Long id;
    @OneToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="CHILD_ID")
    Child child;
}

@Entity
public class Child {
    @Id Long id;
    @OneToOne(mappedBy="child",fetch=FetchType.EAGER)
    Parent parent;
}

然后,您可以查询有一个FK列的Child,从而产生一个选择:

then you can query the Child producing a single select as there is a FK column:

@Test
public void shouldQueryTheDatabaseOnlyOnce() {
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("stackoverflow");
    EntityManager entityManager = emf.createEntityManager();
    Child child = entityManager.find(Child.class, 2l);
    assertEquals((Long)1l, child.parent.id);
}

结果是:

休眠:从父对象parent0_左父项的子代child1_的parent0_.id = child1_.PARENT_ID上选择parent0_.id作为id1_1_0_,child1_.id作为id1_0_1_,child1_.PARENT_ID作为PARENT_I2_0_1_,其中parent0_.id =?

Hibernate: select parent0_.id as id1_1_0_, child1_.id as id1_0_1_, child1_.PARENT_ID as PARENT_I2_0_1_ from Parent parent0_ left outer join Child child1_ on parent0_.id=child1_.PARENT_ID where parent0_.id=?

这篇关于如果定义了双向关系,Hibernate将执行两次相同的查询的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆