使用HQL进行Hibernate分页

Hibernate分页问题

我有一个与Hibernate分页有关的问题,在某种程度上已经解释了这个问题

Mysql分页优化

使用Hibernate的ScrollableResults慢慢读取9000万条记录

Hibernate – HQL分页

分页和排序的问题

Hibernate Row Pagination

细节

来自应用程序的HQL查询:

Query q = session.createQuery("from RequestDao r order by r.id desc"); q.setFirstResult(0); q.setMaxResults(50); 

查询返回300万条记录,对于分页,我们只设置了50条记录,分页页面非常慢,因为在每次刷新时我们都会调用查询来获取300万条记录,而我们只设置50条记录。

我的主要问题是

HQL是否总是进入并命中数据库,或者它是否会进入会话或内存以查找数据,如果它每次都进入数据库并获得结果集那么从性能的角度来看它是非常合适的,什么是最好的解决方案来改进它?

在hibernate中使用HQL有一种方法可以查询数据库,首先只获取50条记录,然后根据用户的要求获取其他记录。 这个挑战真的让应用程序陷入困境,那么解决这个问题的最佳方法是什么呢?

在日志中生成的HQL查询

 from com.delta.dao.RequestDao r order by r.id desc 

Hibernate生成的查询

 select getrequest0_.ID as ID24_, getrequest0_.TIME as START3_24_, getrequest0_.STAT as STATUS24_, getrequest0_.SUM as SUMMARY24_, getrequest0_.OUTNAME as OUTPUT7_24_, getrequest0_.INPNAME as INPUT8_24_, getrequest0_.REQUEST_DATE as requestT9_24_, getrequest0_.PARENT_ID as PARENT10_24_, getrequest0_.INTER_TYPE as INTERPO60_24_, getrequest0_.OPEN_INT as OPEN61_24_, getrequest0_.SOURCE_TYPE as SOURCE62_24_, getrequest0_.TARGET_TYPE as TARGET20_24_, getrequest0_.SOURCE as SOURCE14_24_, getrequest0_.COPY_DATA as COPY16_24_, getrequest0_.CURVE as GENERATE63_24_, getrequest0_.TITLE as TITLE24_, getrequest0_.TIME_ID as TIMESERIES12_24_, getrequest0_.TASK_NAME as TASK51_24_ from REQUEST getrequest0_ where getrequest0_.KIND='csv' order by getrequest0_.ID desc 

以下是查询的解释计划


  |  id |  select_type | 表| 类型|  possible_keys | 关键|  key_len |  ref | 行| 过滤| 额外的| 
  |  1 | 简单|  getrequest0_ |  ref |  TR_KIND_ID |  TR_KIND_ID |  6 |  const |  1703018 |  100.00 | 使用何处|

附加信息:查询50个记录限制的有和没有order by子句的运行时间


如果我with order子句运行查询with order那么查询需要0.0012s并设置LIMIT 50without order子句,同样的查询需要0.0032s且具有相同的LIMIT 50


另外,我们如何找到:

  1. 特定的HQL查询是在命中数据库而不是缓存或从会话中获取信息?
  2. 是不是HQL Query总是会命中数据库以获得结果,而Criteria会进入并点击会话或缓存并从中获取结果?
  3. 另外在我下面提到的查询中:

     a) Query q = session.createQuery("from RequestDao r order by r.id desc"); b) q.setFirstResult(0); c) q.setMaxResults(50); 

在a,我们从数据库获得结果并将其存储在内存中,或者如果没有,并且此时我们在结果集中有300万个结果,然后在b和c我们设置偏移值并限制在页面上我们会这样,这是真的吗?只看到50个结果,所以现在剩下300万个记录,在我们第二次调用这个查询时,我们再次进入数据库并获取300万条记录并将它们放入内存然后在c再次设置50条记录并继续上。

这个问题对我来说并不清楚,因此如果有人可以提供明确而详细的解释,说明这是如何工作以及什么是这个问题的最佳解决方案,我将非常感激。

更新

事实certificate,问题与页面上的记录显示无关,但我在该页面上有filter,并且每次请求都会从数据库中再次获取所有下拉值,并且有一些时髦的事情在那里发生导致页面加载时间增加。

我正在对数据库进行多个嵌套的hibernate查询并获得结果,这个问题的最佳解决方案是什么?

您的查询告诉数据库对满足WHERE子句的所有记录进行排序。 它有可能在返回前50之前对数百万条记录进行排序。

编辑1/26:现在澄清了具体问题,我将尝试更具体地回答。

  1. 每次执行这样的查询时,Hibernate都会进入数据库。 更重要的是,它会将会话中的所有新/更新数据刷新到磁盘。 如果这是您的情况,此行为可能会导致缓慢。

  2. 使用Hibernate查询API通常在大多数情况下都能很好地运行,并且与各种各样的数据库平台兼容。 如果您真的担心从数据访问层中挤出最后一滴性能,您可以编写自己的本机SQL查询来选择前50个结果。 但是一旦你这样做,你几乎肯定会失去数据库独立性。 因此,评估您的成本与收益。

您的查询运行时间似乎在单毫秒范围内。 这通常与在磁盘上存储数据的关系数据库一样好。 所以你可能想评估一下你是否确实遇到了性能问题。

编辑1/27:好的。 它看起来像页面整体设计中的问题。 我在过去7年左右的时间里一直在使用AJAX,因此我不必等待过滤UI控件在浏览表格时重绘。 我想,在您的情况下,切换应用程序UI框架不是一个选项。 您必须弄清楚如何优化下拉列表等数据的加载。 这些数据经常变化吗? 你可以在应用程序的某个地方缓存它吗? 如果你每次都要加载它们,你能不能只获取显示字符串而不是整个对象?

如果设置第一个结果和最大结果数,Hibernate将只从数据库中检索这些条目。 如果这很慢,请将max results设置为1并启用SQL日志记录以查看还加载了哪些关联。

Hibernate还支持几个缓存:*第一级缓存:仅在一个Hibernate会话期间使用,该会话通常对应于一个事务*二级缓存:在会话/事务边界上使用,但大多数只能通过id方法查找*查询缓存:如果启用Hibernate将首先从那里查找查询结果。 但是,查询在HQL和参数方面必须相同,因此每个页面可能会从DB加载一次然后被缓存。 (有关详细信息,请查看此处: http : //docs.jboss.org/hibernate/core/3.5/reference/en/html/performance.html#performance-querycache )

请注意,缓存需要堆内存,并且根据实体的大小,缓存300万个实体可能会导致巨大的内存损失,从而增加垃圾收集,这将再次影响性能。