如果定义了双向关系,Hibernate将执行两次相同的查询

时间:2015-03-18 15:06:16

标签: hibernate hibernate-mapping

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

@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代码是:

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 )链接到父项,该外键也是唯一的。 使用以下代码从数据库中检索子实例:

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

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

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表被连接和获取。我试图将Parent的“child”属性的获取策略设置为LAZY以防止第二个查询,但它没有效果。

如何使用单个查询急切加载双向关系? 实际上,如果检索到N个子实例,则执行N + 1个查询。

1 个答案:

答案 0 :(得分:0)

正如here所指出的,解决方案是使用非拥有方。如果您修改映射,将拥有方移动到Parent(字段包以保护简洁):( / p>)

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

然后您可以查询生成单个选择的Child,因为有一个FK列:

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

结果是:

  

Hibernate:选择parent0_.id为id1_1_0_,child1_.id为id1_0_1_,child1_.PARENT_ID为PARENT_I2_0_1_来自Parent parent0_ left outer join Child child__ on parent0_.id = child1_.PARENT_ID where parent0_.id =?