如何在Hibernate中使用JPA Criteria API摆脱​​N + 1

时间:2018-10-17 08:28:53

标签: java hibernate jpa hibernate-criteria criteria-api

我正在将DAO从使用Hibernate Criteria API迁移到JPA Criteria 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);
}

我得到了正确的结果,所有AB记录都被正确检索。但是,在迁移之后,我遇到了n + 1个问题:尽管在生成的查询中使用了B,但所有LEFT OUTER JOIN记录都是一个接一个地检索的。以前(使用Hibernate Criteria API时),Hibernate能够满足查询,而n + 1在生成的SQL中没有相同的联接。

感谢您的任何想法和帮助!

更新作为示例,使用上面的if-else通过“ b1.fieldName”进行搜索,我将得到以下信息:

criteriaBuilder.equal(root.join("b1", JoinType.LEFT).get("someFiled"), 42)

1 个答案:

答案 0 :(得分:1)

N + 1问题来自@ManyToOne关联中的default EAGER fetching strategy

因此,您需要像这样切换到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个问题,请查看this article

如果您需要使用Criteria API急切地获取关联,则应使用fetch而不是join

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