【发布时间】:2017-06-15 17:22:26
【问题描述】:
我们目前正在研究加快应用程序速度的方法,其中很大一部分与实体列表(实际上是表格)有关。
参数和要求
该列表的参数和要求如下(我将尝试仅在此处提及相关的):
- 列表中最多可以有 500,000 行/实体。
- 一次只显示其中的几个,我们将在此处使用分页。
- 用户可以选择要在列表中显示哪些列(因此我们无法提供单个“静态”查询)。
- 这些列表列中的大多数都是可排序和/或可过滤的。
- 实体具有一对多关系,它们也提供一些列表列。
- 其中一些列表列可以包含多个值(在单个单元格中显示为列表)
- 任何源自当前用户操作(例如编辑、上传等)的数据更新都应尽快反映在列表中(即“立即”)
为了使模型更清晰,请考虑以下简化实体(如 JPA 中的)模型:
class Car {
String manufacturer;
String model;
Date dateOfProduction;
List<TyreSize> allowedTyreSizes;
Set<Date> inspectionDates;
}
请不要试图在该模型中赋予太多含义,因为它只是为了说明问题(我们的数据不同且复杂得多)。
汽车的“完整”列表可能如下所示:
+==============+=======+=======+===============+=============+
| Manufacturer | Model | Prod. | Allowed Tyres | Inspections |
+==============+=======+=======+===============+=============+
| BMW | 320d |01/2016| - 225/40 R18 | - 01/07/16 |
| | | | - 225/45 R17 | - 13/12/16 |
+--------------+-------+-------+---------------+-------------+
| Toyota | Camry |09/2016| - 185/70 R13 | - 31/12/16 |
+--------------+-------+-------+---------------+-------------+
由于用户可以在运行时选择要显示的列,我们正在动态构建必要的查询。到目前为止,这一切都很好。
基本问题
我们遇到的问题是涉及排序和过滤时的性能:我们目前的方法是将排序和过滤所需的所有数据加载到内存中,在那里进行排序和过滤,然后保留排序后的 id 和页面列表那些。我们知道这有点慢,但到目前为止,性能足以满足我们的管理层。不过,由于我们现在有更多数据可供操作,而且性能要求也提高了,因此情况发生了变化。
因此,我们正在研究改进所有数据的排序和过滤的方法,而我们目前正在遵循在数据库上执行此操作的方法,我仍然会问这个(侧面)问题:
- 如何最好地处理具有动态列和每个单元格可能有多个值的大量行的分页?
目前我们正在使用 Postgresql,如果可能的话希望继续使用它,但如果不同的存储更适合我们至少会检查一下。
目前的方法和问题 (在底部)
如上所述,我们目前正在尝试让数据库对我们的数据进行排序、过滤和分页。可以使用 2 个查询:一个用于获取当前页面的行 ID,另一个用于实际加载这些行的数据。
由于挑战是第一个查询,我将专注于:
AFAIK 我们可以在 SQL 中执行类似的操作(使用上面的汽车示例):
SELECT DISTINCT id FROM (
SELECT id, ... FROM car c
LEFT OUTER JOIN allowedtyresizes ats ON c.id = ats.car_id
LEFT OUTER JOIN tyresizes ts ON ts.id = ats.tyresize_id
... //additional joins if required
ORDER BY ... //apply any user-defined sorts
WHERE ... //apply any user-defined filters (or maybe put them into the joins)
)
OFFSET ... //page offset
LIMIT ... //page size
理论上,这个查询(虽然它可能不完全正确)应该提供我们需要的结果,以确定要为当前页面加载哪些行。
由于我们使用的是 Hibernate (5.2 atm),我们希望使用 HQL 或 Criteria 来实现这一点。然而,Hibernate 似乎不支持从上面的 select 语句中进行选择,因此这可能不是一种可行的方法。如果我们不得不回退到原生 SQL 或完全不同的方法,那就这样吧,但我们更愿意让它与现有的基础设施一起工作。
所以问题是:
- Hibernate 5.x 是否支持“从选择中选择”之类的功能?
- 当必须对多对关系进行排序和过滤时,如何使用 Hibernate 进行分页,即单个连接可能导致重复行?
【问题讨论】:
-
您使用 HQL 运行 SQL 查询,方法是将查询作为字符串传递给它
-
@GauravSrivastav 是的,我们知道如何执行 SQL 查询,如果没有更好的方法,我们将使用它。然而,由于查询可能变得相当复杂并且是动态创建的,我们宁愿不必访问我们实体的映射信息来提取构建此类查询所需的信息 - 如果 Hibernate 已经提供了我们可以使用的东西,则不会。
-
您的模型有多复杂?通过使用数据库视图并将实体绑定到它们,您可能会获得最佳性能。坏事是您需要手写数据库视图(如果您开始支持新的 RDBMS,可能还有另一个版本)。好消息是实体会变得更简单,Hibernate 生成性能不佳的 SQL 的机会也会减少。
-
@MickMnemonic 该模型非常复杂,有 10 多个表可能会在多个级别上连接(例如实体->类别->文本)和一些可以在运行时创建/添加的数据列并且在某种程度上仿照entity abstraction pattern(被某些人认为是反模式,仅用于实际需要它的部分)。
-
@MickMnemonic 视图可能是我们模型的主要部分的一个选项,它的结构在运行时是静态的。你将如何处理潜在的多对多关系?它们会以某种方式被建模为数组列吗?
标签: java hibernate pagination