使用连接

时间:2017-01-30 14:04:40

标签: java hibernate pagination

我们目前正在研究加速应用程序的方法,其中很大一部分与实体的列表(实际上是表格)有关。

参数和要求

该列表的参数和要求如下(我将尝试仅在此处提及相关内容):

  • 列表中最多可包含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是否支持类似“select from select”的内容?
  • 如果必须对多对多关系进行排序和过滤,那么如何使用Hibernate进行分页,即单个连接可能导致重复的行?

1 个答案:

答案 0 :(得分:2)

我在过去的雇主中有类似的要求,我们也注意到,在较小的数据集下,数据库能够做到这一点;然而,即使是数据库也会受到影响。

我的解决方案是介绍Hibernate Search及其与ElasticSearch的集成,以将搜索数据存储在NoSQL Lucene数据存储区中,这对于基于unicode的文本查询和排序来说绝对快速,正如您所描述的那样。

这使您可以继续使用已有的Hibernate ORM基础架构,并以最小的努力将这些附加组件烘焙到您的架构中。集成是无缝的,当然值得投资,特别是当您的数据集随着时间的推移而不断增长时。

我们正在使用数百万行,并且绝对没有性能问题。执行排序和分页的查询平均花费不到100毫秒。