【问题标题】:How to prevent Hibernate from flushing in list?如何防止 Hibernate 在列表中刷新?
【发布时间】:2013-01-18 16:42:12
【问题描述】:

我有一个简单的 Hibernate 查询,例如:

from MyEntity where name = ?

没什么花哨的,但它在相当大的事务中被多次调用(持续一秒钟,可能加载数十或数百个实体)。 Profiler 显示大量时间花在:

org.hibernate.internal.SessionImpl.autoFlushIfRequired(SessionImpl.java:1185)
org.hibernate.internal.SessionImpl.list(SessionImpl.java:1240)
org.hibernate.internal.QueryImpl.list(QueryImpl.java:101)

换句话说 - 在运行实际查询之前刷新更改。

我能否以某种方式阻止 Hibernate 执行此刷新操作?

如果没有,我该怎么做才能让它更快?

【问题讨论】:

  • 您是否使用不同的绑定参数运行查询?查询返回一个结果还是多个?
  • nMoncho - 在此会话中每次都使用相同的参数,返回一个唯一的结果。是的,我确实使用可缓存查询。
  • 可能是只读事务?也许将整个实体标记为只读(不记得如何制作)。同样,一旦您第一次获取它,您就会知道 ID。通过 PK 获取缓存在 L1 中,应该更智能。最后考虑更高级别的缓存,例如 Spring 中的@Cacheable。最后但同样重要的是,尝试分离该实体,Hibernate 会忘记它。

标签: java hibernate


【解决方案1】:

默认情况下,hibernate 在会话期间发出查询之前会刷新 (FlushMode.AUTO),这样做会占用大量 CPU 时间。如果您有一个会话,其中许多查询和更新交替运行,那就特别痛苦。

如果查询可能会选择您在当前会话期间插入/更新/删除的数据,那么您需要这些刷新。即使您没有修改当前事务中的任何内容但正在使用诸如未提交的事务隔离级别,可能也会出现这种情况。

假设您不想要未提交的读取等,我知道有两种解决方法:

  1. 在进行任何修改之前选择您在会话期间需要的所有内容(即重新排序,以便在进行任何修改之前发出您的所有查询)。
  2. 如果您确定您正在选择在此会话中未修改的数据,那么您可以将刷新模式显式设置为不会触发刷新的内容,如下所示:

    Query query = session.getNamedQuery(SOME_QUERY_NAME);
    query.setFlushMode(FlushMode.COMMIT);
    

【讨论】:

  • 不管FlushMode,如果 Hibernate 检测到您正在查询自 3.3 以来更改的表,它仍会刷新。
【解决方案2】:

Hibernate 在 3.3 版中更改了其刷新逻辑。

现在,即使设置为FlushMode.MANUAL,如果它检测到您正在查询存在脏实体的表,它将始终刷新。

我能想到的一些解决方法:

  • 重新排序操作以不查询脏表并设置FlushMode.COMMIT 或更低。
  • 在修改前分离实体,稍后重新附加,这样 Hibernate 就不会知道它们已被更改。
  • 提供具有不同行为的自定义AutoFlushEventListener。这将需要一些低级的摆弄,并且可能会破坏其他东西。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-10-21
    • 2013-12-25
    • 2020-01-25
    • 2010-10-18
    • 2014-02-14
    • 1970-01-01
    • 1970-01-01
    • 2011-11-22
    相关资源
    最近更新 更多