Hibernate performs twice the same query if a bidirectional relationship is defined

余生长醉 提交于 2021-01-03 03:29:11

问题


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

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

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 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 = ?;

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.

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.


回答1:


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

and

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

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

The result is:

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=?



来源:https://stackoverflow.com/questions/29125588/hibernate-performs-twice-the-same-query-if-a-bidirectional-relationship-is-defin

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!