java - 如何在 Hibernate 中使用 JPA Criteria API 摆脱 N+1
问题描述
我正在将我们的 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);
}
我得到了正确的结果,所有A
和B
记录都被正确检索。但是,在迁移之后,我遇到了 n+1 问题:尽管在生成的查询中使用了 's,但所有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)
解决方案
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
.
如果您需要使用 Criteria API 急切地获取关联,那么您应该使用fetch
而不是join
.
if (LEFT_OUTER_JOIN_ENTITIES.contains(field)) {
path = ((From) path).fetch(field, JoinType.LEFT);
} else {
path = path.get(field);
}
推荐阅读
- python - 有没有办法让外键从一个属性到另一个来自 django 中的其他类?
- angular - 调用对象 OnInit 的 contentdocument
- javascript - 如何让 PeerJS 在现实世界中工作?
- java - javax.swing.text.html.HTMLDocument:如何在文档中找到 html 图像映射?
- android - a里面的竖线
标签对 RecyclerView 中的子项不可见 - elasticsearch - 这是使用 Elasticsearch 的错误方式吗?
- deeplearning4j - DL4J - 有没有办法限制模型的预测
- php - PDFMerger (PHP) 只是没有运行 - 我做错了什么?
- java - 如果调用的方法在 Java 代码的 IntelliJ 调试器中抛出异常,则停止调试点
- tensorflow - spyder 和 jupyter 中的 Keras(Tensorflow) LSTM 错误