【问题标题】:Hibernate criteria query taking time to fetch records from data base table休眠条件查询需要时间从数据库表中获取记录
【发布时间】:2019-01-04 12:34:21
【问题描述】:

在我的项目中,我将 10000 条记录插入到表中。如果任何记录已经可用,我们将通过使用休眠条件获取它并将其与新记录进行比较。我们在表中的数据非常少,大约 10000 条记录。对于休眠标准,我们已经应用了条件。此休眠条件从表中获取 6 条记录。但是,如果我调用 criteria.list() 方法来获取 6 条记录,为什么要花时间

HIbernate 条件查询:

select this_.CLASS_TYPE as CLASS1_15_0_, this_.CLASS_UNIT_TYPE as CLASS2_15_0_, this_.CLASS_UNIT_CODE as CLASS3_15_0_, 
this_.ID as ID15_0_, this_.LANG_CODE_ISO as LANG5_15_0_, this_.CTY_CODE_ISO as CTY6_15_0_, this_.NAME as NAME15_0_, this_.INS_DTIME as INS8_15_0_,this_.UPD_DTIME as UPD9_15_0_, this_.DEL_DTIME as DEL10_15_0_ from FI_SALES_LOCATION_V this_ where (this_.CLASS_TYPE='BU' and this_.CLASS_UNIT_TYPE='STO' and this_.CLASS_UNIT_CODE='A1000') and this_.DEL_DTIME is null and ((this_.LANG_CODE_ISO='en' and this_.CTY_CODE_ISO='GB' and this_.ID='A1000') or (this_.LANG_CODE_ISO='en' and this_.CTY_CODE_ISO='GB' and this_.ID='AP1000') or (this_.LANG_CODE_ISO='pt' and this_.CTY_CODE_ISO='PT' and this_.ID='AP1000') or (this_.LANG_CODE_ISO='pt' and this_.CTY_CODE_ISO='PT' and this_.ID='A1000') or (this_.LANG_CODE_ISO='s1' and this_.CTY_CODE_ISO='SI' and this_.ID='AP1000') or (this_.LANG_CODE_ISO='s1' and this_.CTY_CODE_ISO='SI' and this_.ID='A1000'));

【问题讨论】:

  • 你确定是休眠需要时间吗?您是否尝试直接在数据库控制台上调用此 SQL 查询(不是休眠条件查询)?如果这不需要时间,我想知道所使用的 DBMS 的公司或产品的名称。因为他们发明了一种创新的颠覆性技术。
  • 如果我在数据库控制台中执行相同的查询,我不会花时间立即得到结果
  • 您能否更好地描述一下您是如何插入记录和验证现有记录的?您是否通过持久/保存插入每条记录?您是否在会话工厂中配置了批处理?您是否在每次插入后验证您的表格?
  • 首先,如果记录是否可用,我们将从表中获取记录。如果可用,我们正在更新该记录的任何更新。否则我们将在表中插入新记录。是的,我们在插入表格后进行验证。我们正在使用 merge() 方法进行更新和插入

标签: hibernate hibernate-mapping criteria hibernate-criteria


【解决方案1】:

考虑到来自问题和 cmets 的信息,您的代码是这样的:

  • 对于要插入的每条记录,您都可以通过条件进行选择。
  • 进行检查,然后根据具体情况保留或合并记录。

您必须了解休眠会话在某些情况下的行为方式。当您在休眠中执行操作时,您正在做的(在第一时间)是更新休眠会话。无论是在插入、删除还是更新中,操作都将在会话中执行,Hibernate 甚至会记录一条 SQL 语句,但 它不会立即刷新到您的数据库(是的,它具有误导性)。

通常一切都被刷新并结束事务,但在某些情况下,hibernate 会刷新操作以使会话保持最新。在您的情况下,您有多个要插入/合并的记录,但在每次操作之前执行选择。 Hibernate 检测到这个操作并且会刷新每个之前存储的操作,仅仅是因为如果有挂起的操作要刷新,查询数据库是没有意义的。数据库需要处于可能的最新状态。不要试图与之抗争,它旨在以这种方式工作。

所以我最好的猜测是,当您的 select 执行时间过长时,这是因为 hibernate 正在刷新之前的插入/更新操作

如何解决:

我想不出任何直接的方法来解决这个问题。您将不得不重新考虑您的方法。

有一件很重要的事情:即使您只需要在没有任何验证的情况下进行纯插入,您也会遇到一些麻烦,因为您的代码试图插入数千条记录。最大的问题是,就像我之前写的那样,hibernate 直到事务结束才会刷新操作。因此,它会将每个要执行的语句保存在内存中。您很有可能会耗尽内存。

我要做的第一件事是检查hibernate docs on batching(从docs开始)。之后,我会想出一个策略来优化插入大量记录的需求和所需的验证。 与其处理单个记录,不如处理数据块。

根据可用的少量信息,我会尝试这个工作流程:

在一切之前,为 hibernate 定义批处理大小属性(再次检查批处理文档)

  • 在处理数据时,使用为批量大小定义的相同大小的数据切片
  • 在要插入的记录中选择要验证的所有可能记录
  • 执行所需的验证以检查切片中的每条记录是合并还是插入
  • 组插入和合并并执行操作。刷新和清除休眠会话
  • 获取下一个数据片段,然后重复该过程

基本上这是您正在执行的“相同”过程,但它将针对记录块执行,而不是在每条记录中执行。

【讨论】:

  • 嗨 Vitor,在创建标准之前,我将删除会话中已经可用的实体,然后性能得到改进 Map entity = ((SessionImplementor) getSession()).getPersistenceContext().getEntityEntries(); if (entities != null)entities.clear();
  • 看起来您正在清除会话,但以一种我以前从未见过的方式(通常我看到 session.clear() 或 entityManager.clear() )。在清除之前刷新 session/entityManager 很重要,否则您将面临失去一致性的风险。我倾向于说这是不正确的。如果您打算这样做,请进行一轮认真的测试。
【解决方案2】:

尝试这样写查询:

select this_.CLASS_TYPE as CLASS1_15_0_, this_.CLASS_UNIT_TYPE as CLASS2_15_0_, this_.CLASS_UNIT_CODE as CLASS3_15_0_, 
this_.ID as ID15_0_, this_.LANG_CODE_ISO as LANG5_15_0_, this_.CTY_CODE_ISO as CTY6_15_0_, this_.NAME as NAME15_0_, this_.INS_DTIME as INS8_15_0_,this_.UPD_DTIME as UPD9_15_0_, this_.DEL_DTIME as DEL10_15_0_ 
from FI_SALES_LOCATION_V this_ 
where 
(this_.CLASS_TYPE='BU' and this_.CLASS_UNIT_TYPE='STO' and this_.CLASS_UNIT_CODE='A1000') 
and this_.DEL_DTIME is null 
and (this_.LANG_CODE_ISO='en' and this_.CTY_CODE_ISO='GB' and this_.ID='A1000') 
UNION ALL
select this_.CLASS_TYPE as CLASS1_15_0_, this_.CLASS_UNIT_TYPE as CLASS2_15_0_, this_.CLASS_UNIT_CODE as CLASS3_15_0_, 
this_.ID as ID15_0_, this_.LANG_CODE_ISO as LANG5_15_0_, this_.CTY_CODE_ISO as CTY6_15_0_, this_.NAME as NAME15_0_, this_.INS_DTIME as INS8_15_0_,this_.UPD_DTIME as UPD9_15_0_, this_.DEL_DTIME as DEL10_15_0_ 
from FI_SALES_LOCATION_V this_ 
where 
(this_.CLASS_TYPE='BU' and this_.CLASS_UNIT_TYPE='STO' and this_.CLASS_UNIT_CODE='A1000') 
and this_.DEL_DTIME is null 
and (this_.LANG_CODE_ISO='en' and this_.CTY_CODE_ISO='GB' and this_.ID='AP1000') 
UNION ALL
select this_.CLASS_TYPE as CLASS1_15_0_, this_.CLASS_UNIT_TYPE as CLASS2_15_0_, this_.CLASS_UNIT_CODE as CLASS3_15_0_, 
this_.ID as ID15_0_, this_.LANG_CODE_ISO as LANG5_15_0_, this_.CTY_CODE_ISO as CTY6_15_0_, this_.NAME as NAME15_0_, this_.INS_DTIME as INS8_15_0_,this_.UPD_DTIME as UPD9_15_0_, this_.DEL_DTIME as DEL10_15_0_ 
from FI_SALES_LOCATION_V this_ 
where 
(this_.CLASS_TYPE='BU' and this_.CLASS_UNIT_TYPE='STO' and this_.CLASS_UNIT_CODE='A1000') 
and this_.DEL_DTIME is null 
and (this_.LANG_CODE_ISO='pt' and this_.CTY_CODE_ISO='PT' and this_.ID='AP1000') 
UNION ALL
select this_.CLASS_TYPE as CLASS1_15_0_, this_.CLASS_UNIT_TYPE as CLASS2_15_0_, this_.CLASS_UNIT_CODE as CLASS3_15_0_, 
this_.ID as ID15_0_, this_.LANG_CODE_ISO as LANG5_15_0_, this_.CTY_CODE_ISO as CTY6_15_0_, this_.NAME as NAME15_0_, this_.INS_DTIME as INS8_15_0_,this_.UPD_DTIME as UPD9_15_0_, this_.DEL_DTIME as DEL10_15_0_ 
from FI_SALES_LOCATION_V this_ 
where 
(this_.CLASS_TYPE='BU' and this_.CLASS_UNIT_TYPE='STO' and this_.CLASS_UNIT_CODE='A1000') 
and this_.DEL_DTIME is null 
and (this_.LANG_CODE_ISO='pt' and this_.CTY_CODE_ISO='PT' and this_.ID='A1000') 
UNION  ALL
select this_.CLASS_TYPE as CLASS1_15_0_, this_.CLASS_UNIT_TYPE as CLASS2_15_0_, this_.CLASS_UNIT_CODE as CLASS3_15_0_, 
this_.ID as ID15_0_, this_.LANG_CODE_ISO as LANG5_15_0_, this_.CTY_CODE_ISO as CTY6_15_0_, this_.NAME as NAME15_0_, this_.INS_DTIME as INS8_15_0_,this_.UPD_DTIME as UPD9_15_0_, this_.DEL_DTIME as DEL10_15_0_ 
from FI_SALES_LOCATION_V this_ 
where 
(this_.CLASS_TYPE='BU' and this_.CLASS_UNIT_TYPE='STO' and this_.CLASS_UNIT_CODE='A1000') 
and this_.DEL_DTIME is null 
and (this_.LANG_CODE_ISO='s1' and this_.CTY_CODE_ISO='SI' and this_.ID='AP1000') 
UNION ALL
select this_.CLASS_TYPE as CLASS1_15_0_, this_.CLASS_UNIT_TYPE as CLASS2_15_0_, this_.CLASS_UNIT_CODE as CLASS3_15_0_, 
this_.ID as ID15_0_, this_.LANG_CODE_ISO as LANG5_15_0_, this_.CTY_CODE_ISO as CTY6_15_0_, this_.NAME as NAME15_0_, this_.INS_DTIME as INS8_15_0_,this_.UPD_DTIME as UPD9_15_0_, this_.DEL_DTIME as DEL10_15_0_ 
from FI_SALES_LOCATION_V this_ 
where 
(this_.CLASS_TYPE='BU' and this_.CLASS_UNIT_TYPE='STO' and this_.CLASS_UNIT_CODE='A1000') 
and this_.DEL_DTIME is null 
and (this_.LANG_CODE_ISO='s1' and this_.CTY_CODE_ISO='SI' and this_.ID='A1000');

即使写得更多,执行 UNION ALL 而不是 OR 时性能会有所提高。

或者你可以索引表,但是你会在 INSERT 上失去性能。

【讨论】:

  • 你好,上面的查询是hibernate在运行时生成的
  • 那么,最好的方法是使用以下列在表上创建索引:LANG_CODE_ISO、CTY_CODE_ISO、ID。性能将在 OR 条件下得到改善。
猜你喜欢
  • 2015-04-17
  • 1970-01-01
  • 1970-01-01
  • 2017-05-23
  • 2011-04-30
  • 2012-03-24
  • 1970-01-01
  • 1970-01-01
  • 2014-12-02
相关资源
最近更新 更多