代码之家  ›  专栏  ›  技术社区  ›  Dmitry Senkovich

如何在Hibernate中用JPA标准API摆脱N+1

  •  2
  • Dmitry Senkovich  · 技术社区  · 6 年前

    我正在将我们的DAO从使用Hibernate标准API迁移到JPA标准API。我有一节课有好几节课 @ManyToOne 在那里:

    @Entity
    @Table(name = "A_TABLE", schema="SCHEMA_NAME")
    public class A {
        @ManyToOne
        @JoinFormula("(SELECT * FROM (SELECT B.B_ID FROM SCHEMA_NAME.B_TABLE B WHERE B.A_ID = B_ID AND (B.B_CODE = '1' OR B.B_CODE = '2') ORDER BY B.B_CREATED_TIMESTAMP DESC) WHERE ROWNUM = 1)")
        private B b1;
        @ManyToOne
        @JoinFormula("(SELECT * FROM (SELECT B.B_ID FROM SCHEMA_NAME.B_TABLE B WHERE B.A_ID = B_ID AND (B.B_CODE = '3' OR B.B_CODE = '4') ORDER BY B.B_CREATED_TIMESTAMP DESC) WHERE ROWNUM = 1)")
        private B b2;
        ...
    }
    
    @Entity
    @Table(name = "B_TABLE", schema="SCHEMA_NAME")
    public class B {
    
    }
    

    在我使用的查询中 JoinType.LEFT 为了摆脱默认生成的 CROSS JOIN

    if (LEFT_OUTER_JOIN_ENTITIES.contains(field)) {
        path = ((From) path).join(field, JoinType.LEFT);
    } else {
        path = path.get(field);
    }
    

    A B 正确检索记录。但是,在迁移之后,我遇到了n+1问题:所有 B 尽管使用了 LEFT OUTER JOIN 在生成的查询中。以前(当使用Hibernate标准API时),Hibernate能够满足查询,而n+1在生成的SQL中没有相同的连接。

    谢谢你的建议和帮助!

    作为使用 if-else

    criteriaBuilder.equal(root.join("b1", JoinType.LEFT).get("someFiled"), 42)
    
    1 回复  |  直到 6 年前
        1
  •  2
  •   Vlad Mihalcea    4 年前

    N+1问题来自默认值 FetchType.EAGER 系统的取数策略 @ManyToOne 联想。

    FetchType.LAZY ,如下所示:

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinFormula("(SELECT * FROM (SELECT B.B_ID FROM SCHEMA_NAME.B_TABLE B WHERE B.A_ID = B_ID AND (B.B_CODE = '1' OR B.B_CODE = '2') ORDER BY B.B_CREATED_TIMESTAMP DESC) WHERE ROWNUM = 1)")
    private B b1;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinFormula("(SELECT * FROM (SELECT B.B_ID FROM SCHEMA_NAME.B_TABLE B WHERE B.A_ID = B_ID AND (B.B_CODE = '3' OR B.B_CODE = '4') ORDER BY B.B_CREATED_TIMESTAMP DESC) WHERE ROWNUM = 1)")
    private B b2;
    

    如果要自动检测可能影响应用程序其他部分的N+1问题,则可以使用 datasource-proxy .

    如果您迫切需要使用criteriaapi获取关联,那么应该使用 fetch join .

    if (LEFT_OUTER_JOIN_ENTITIES.contains(field)) {
        path = ((From) path).fetch(field, JoinType.LEFT);
    } else {
        path = path.get(field);
    }