即使设置了FetchType.EAGER + FetchMode.JOIN,Hibernate也会为“钻石形” OneToMany关系创建N + 1个选择

时间:2018-07-05 20:32:52

标签: hibernate

我需要在单个查询中获取一个具有多个子节点层的复杂对象,直到最后一片叶子(最好)。每个父母与他们各自的孩子都有{"Name": "Bob Smith", "Address": "123 main st", "Telephone": "111-111-1111"} {"Name": "Jon Smith", "Address": "123 main st", "Telephone": "111-111-1111", "extra": ["222-222-2222"]} 关系。

为了实现这一点,我试图在所有@OneToMany关系上使用FetchType.EAGERFetchMode.JOIN,所以当我获取最顶层的对象时,Hibernate将生成一个包含以下内容的查询:所有的孩子。但是,不幸的是,我遇到了N + 1问题。我相信这是因为我的实体之间的关系形成了“菱形”,就像这样:

diamond structure diagram

当我如图所示映射关系时,Hibernate会生成N + 1个查询。但是,如果我删除与@OneToMany的两个关系之一,则一切正常,并且Hibernate在单个查询中完成了工作(符合预期)。

以下是重现该问题的一些最小代码:

BOTTOM

还有我用来获取@Entity @Table(name = "Top") public class Top { @Id String id; @OneToMany(mappedBy = "parent", fetch = FetchType.EAGER) @Fetch(value = FetchMode.JOIN) Set<Left> left; @OneToMany(mappedBy = "parent", fetch = FetchType.EAGER) @Fetch(value = FetchMode.JOIN) Set<Right> right; } @Entity @Table(name = "Left") public class Left { @Id String id; String parent; @OneToMany(mappedBy = "parent", fetch = FetchType.EAGER) @Fetch(value = FetchMode.JOIN) Set<Bottom> bottom; } @Entity @Table(name = "Right") public class Right { @Id String id; String parent; @OneToMany(mappedBy = "parent", fetch = FetchType.EAGER) @Fetch(value = FetchMode.JOIN) Set<Bottom> bottom; } @Entity @Table(name = "Bottom") public class Bottom{ @Id String id; String parent; } 对象的代码:

TOP

正在生成的SQL如下所示:

SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();

//Some arbitrary known "Top" object
session.get(Top.class, "1");

session.getTransaction().commit();
session.close();

有人知道需要更改什么以便Hibernate生成单个查询而不是N + 1吗? SELECT top0_.id AS id1_3_0_, left1_.parent AS parent2_1_1_, left1_.id AS id1_1_1_, left1_.id AS id1_1_2_, left1_.parent AS parent2_1_2_, bottom2_.parent AS parent2_0_3_, bottom2_.id AS id1_0_3_, bottom2_.id AS id1_0_4_, bottom2_.parent AS parent2_0_4_, right3_.parent AS parent2_2_5_, right3_.id AS id1_2_5_, right3_.id AS id1_2_6_, right3_.parent AS parent2_2_6_ FROM Top top0_ LEFT OUTER JOIN Left left1_ ON top0_.id = left1_.parent LEFT OUTER JOIN Bottom bottom2_ ON left1_.id = bottom2_.parent LEFT OUTER JOIN Right right3_ ON top0_.id = right3_.parent WHERE top0_.id = ? SELECT bottom0_.parent AS parent2_0_0_, bottom0_.id AS id1_0_0_, bottom0_.id AS id1_0_1_, bottom0_.parent AS parent2_0_1_ FROM Bottom bottom0_ WHERE bottom0_.parent = ? id列的这种双重选择可能已经是映射某些基本问题的症状了吗?

顺便说一句,我正在使用Hibernate 5.3.2.Final。

谢谢!

1 个答案:

答案 0 :(得分:0)

在父类中使用@OneToMany时(@ManyToOne更好的选择是btw) 您需要使用双向关系。否则,您将拥有多个查询。 因此,添加每个孩子类@ManyToOne,然后让他们进行映射。

例如:

@Entity
@Table(name = "Top")
public class Top {
    @Id
    String id;

    @OneToMany(mappedBy = "top", fetch = FetchType.EAGER)
    List<Left> left = new ArrayList<>();

    @OneToMany(mappedBy = "top", fetch = FetchType.EAGER)
    List<Right> right = new ArrayList<>();
}

@Entity
@Table(name = "Left")
public class Left {
    @Id
    String id;
    String parent;

    @ManyToOne
    @JoinColumn(name = "top_id")
    Top top;

    @OneToMany(mappedBy = "parent", fetch = FetchType.EAGER)
    Set<Bottom> bottom;
}

@Entity
@Table(name = "Right")
public class Right {
    @Id
    String id;
    String parent;

    @ManyToOne
    @JoinColumn(name = "top_id")
    Top top;

    @OneToMany(mappedBy = "parent", fetch = FetchType.EAGER)
    Set<Bottom> bottom;
}