在我当前的项目中,我们在系统中有多个搜索页面,我们从数据库中获取大量数据以显示在 UI 中的大表格元素中。我们使用 JPA 进行数据访问(我们的提供者是 Hibernate)。大多数页面的数据是从多个数据库表中收集的——在许多情况下大约有 10 个——包括来自 OneToMany 关系的一些聚合数据(例如“类型 X 的关联实体的数量”)。为了提高性能,我们使用结果集分页TypedQuery.setFirstResult()
和TypedQuery.setMaxResults()
当用户滚动表格时,延迟加载数据库中的其他行。由于搜索非常动态,我们使用 JPA CriteriaQuery API 来构建查询。但是,我们目前在某种程度上受到 N+1 SELECT 问题的困扰。实际上在某些情况下这很糟糕,因为我们可能会迭代 3 层嵌套的 OneToMany 关系,其中每一层的数据都是延迟加载的。我们不能真正将这些集合声明为在实体映射中预先加载,因为我们只在某些页面中对它们感兴趣。即,我们可能从几个不同页面中的同一个表中获取数据,但我们在不同页面中显示来自表和不同关联表的不同数据。
为了缓解这个问题,我们开始尝试使用 JPA 实体图,它们似乎对 N+1 SELECT 问题有很大帮助。但是,当您使用实体图时,Hibernate 显然会在内存中应用分页。我可以在一定程度上理解它为什么会这样做,但是在许多情况下,这种行为否定了实体图的很多(如果不是全部)好处。当我们不使用实体图时,我们可以在不应用任何 WHERE 限制的情况下加载数据(即,将整个表视为结果集),无论该表有几百万行,因为只有非常有限的行数实际上是由于分页而被提取的。现在分页是在内存中完成的,Hibernate 基本上会获取整个数据库表(加上实体图中定义的所有关系),然后在内存中应用分页,扔掉其余的行。不好。
所以问题是,是否有一种有效的方法可以使用 JPA(Hibernate)同时应用分页和实体图?如果 JPA 没有为此提供解决方案,那么特定于 Hibernate 的扩展也是可以接受的。如果这也不可能,那么其他选择是什么?使用数据库视图?视图会有点麻烦,因为我们支持多个数据库供应商。为不同的供应商创建所有必要的视图会大大增加开发工作量。
我的另一个想法是像我们目前一样应用实体图和分页,如果它们返回太多行,则根本不触发任何查询。我已经需要执行 COUNT 次查询以使行的延迟加载在 UI 中正常工作。