在我当前的项目中,我们在系统中有多个搜索页面,我们从数据库中获取大量数据以显示在UI中的大表格元素中。我们使用JPA进行数据访问(我们的提供者是Hibernate)。大多数页面的数据都是从多个数据库表中收集的 - 在许多情况下大约为10个 - 包括来自OneToMany关系的一些汇总数据(例如“类型X的关联实体的数量”)。为了提高性能,我们使用TypedQuery.setFirstResult()
和TypedQuery.setMaxResults()
的结果集分页,以便在用户滚动表格时从数据库中延迟加载额外的行。由于搜索非常动态,因此我们使用JPA CriteriaQuery API构建查询。但是,我们目前有些受到N + 1 SELECT问题的困扰。在某些情况下,它确实很糟糕,因为我们可能会迭代3层嵌套的OneToMany关系,其中每个级别的数据都是延迟加载的。我们不能真正将这些集合声明为在实体映射中加载的,因为我们只在某些页面中对它们感兴趣。即我们可能会在几个不同的页面中从同一个表中获取数据,但是我们会在表中显示不同的数据,并显示不同页面中的不同关联表。JPA实体图表和分页
为了减轻这一点,我们开始尝试使用JPA实体图,并且它们似乎对N + 1 SELECT问题有很大帮助。但是,当您使用实体图时,Hibernate显然会在内存中应用分页。我可以稍微理解为什么它会这样做,但是这种行为在许多情况下否定了很多(如果不是全部的话)实体图的好处。当我们不使用实体图时,无论应用任何WHERE限制(即将整个表视为结果集),我们都可以加载数据,无论表中有多少百万行,因为只有非常有限的行实际上由于分页而被提取。既然分页是在内存中完成的,Hibernate基本上会获取整个数据库表(加上在实体图中定义的所有关系),然后在内存中应用分页,将剩余的行丢弃。不好。
所以问题是,有没有一种有效的方法来应用分页和实体图与JPA(休眠)?如果JPA没有提供解决方案,那么Hibernate特定的扩展也是可以接受的。如果这也不可能,那么其他的选择是什么?使用数据库视图?由于我们支持多个数据库供应商,所以视图会有点麻烦。为不同的供应商创建所有必要的视图会大大增加开发工作量。
我的另一个想法是应用实体图和分页,就像我们目前所做的那样,如果它们返回的行太多,根本不会触发任何查询。我已经需要做COUNT查询才能让行的延迟加载在UI中正常工作。
你的情况听起来很相似,我们(我们也有一大堆的动态过滤器和需要分页之前要应用排序规则)。我实际上考虑过尝试类似的解决方案,但认为这样做效率太低。但显然它不是?因此,在第2步中,您使用主要实体的主键在查询中定义了一个IN条件?我有点担心,我可能会遇到[HHH-9230(https://hibernate.atlassian.net/browse/HHH-9230)如果我这样做(我们仍然在使用Hibernate 4.x版)。再说,你永远不知道,直到你试试:) –
我尝试了你提出的办法,似乎工作得很好。至少我最初的印象是它比Hibernate的内存分页快很多,但我还没有做过任何具体的测量。我没碰上[HHH-9374(https://hibernate.atlassian.net/browse/HHH-9374)运行时,某些查询,虽然,但我希望升级Hibernate会解决。谢谢你的回答! –
@MarkusYrjölä我很高兴听到它的作品。我们也使用4.x,所以它应该可以工作(尽管我们没有使用实体图)。我想补充的一件事是:由于第二个查询的where条件可能会导致相当大的声明,我们通常将其拆分并发出多个查询。根据我们的经验,在“更小”查询/结果导致性能提升超过您通过使用多个查询而不是一个查询而获得的处罚的情况下,会有一个甜蜜点/区域。甜蜜点可能会有所不同,但250-500的大小对我们来说工作得相当好。 – Thomas