【问题标题】:efficient queries for the use of hibernate cache使用休眠缓存的高效查询
【发布时间】:2011-12-30 04:39:58
【问题描述】:

我使用 JPA 和 Hibernate 实现。对于有经验的 hibernate 用户来说,我的问题可能是基本的:基于 Hibernate 一级缓存编写查询的最有效方法是什么?

例如,我有实体A和实体B

@Entity
class A{
      private int ida;
      private int x;
      private String s;
    @OneToMany(mappedBy = "ida", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
      private Set<B> Bset;
    }

@Entity
class B{
      private int ida;
      private String s2;
    }

假设我有几个可能在同一个会话中发生的流程:

  1. 获取A.x
  2. 获取整个A实体
  3. 检查A是否包含Bs2="..."

对于这些查询中的每一个我都可以

  • 编写特定查询,通过idas2 获取A.x / B
  • 假设Hibernate保存缓存并且总是得到A对象,或者A.Bset()然后java循环在Bset()里面得到需要的B

什么是最有效的方法?

谢谢

【问题讨论】:

    标签: hibernate caching jpa-2.0


    【解决方案1】:

    对于用例 1 和 2,我只加载整个实体。二级缓存同样可以正常工作。除非您的 A 对象非常大(具有 很多 属性),否则您不会看到与以下相比有任何区别:

    SELECT a.x FROM A a WHERE a.id = :id
    

    更糟糕的是,使用上面的查询不会利用 L2 缓存。

    第三个用例更有趣。这在很大程度上取决于您的要求,但合理的平衡是使用这样的查询:

    SELECT B b
    WHERE b.s2 == :s2
      AND b.a = :a
    

    如果查询返回某些内容,则意味着a 包含b 和给定的s2。这应该比延迟加载Bset 并对其进行迭代要快得多。考虑启用查询缓存。

    但是,如果 Bset 通常很小并且您使用急切获取,那么 Java 中的简单过滤可能会更好。这真的取决于你的架构。

    【讨论】:

    • 谢谢,这解决了我的一些用例。如果一旦我需要一个 B 行,我将需要整个 Bset,推荐什么?我认为急切的获取会为 A.Bset 带来 B 行。
    • 不,Eager fetching 实际上是用来防止一对一的抓取(臭名昭著的 N+1 问题)。通过急切获取,Hibernate 将为您创建一个 JOIN 查询,并使用一条 SQL 一次获取所有 Bs 的 A
    • 这是使用@FetchMode(JOIN)配置的
    【解决方案2】:

    只是一个警告,我认为上述 A 和 B 之间的映射不起作用。当您在关系的 OneToMany 端使用 mappedBy 时,您还需要在 B 中映射反向关系。

    @Entity
    class B{
          @ManyToOne
          @JoinColumn(name="aid")
          private A a;
          private String s2;
        }
    

    不建议使用 OneToMany 单向关系。

    在回答您的问题时,有效使用会话缓存的关键是尽可能使用 get()。因此,如果您可以通过其 @Id 获取 A,您可以遍历 B 的映射集合以找到您感兴趣的实例。

    使用 get 将确保如果您所追求的 A 实例在缓存中,它将被返回(非常快),如果它不在缓存中,它将被添加到缓存中,然后返回给您。然后下次你通过@Id 请求它时,它将从缓存中返回。

    每当您使用 HQL 并强制 Hibernate 执行查询时,您将绕过 Session 缓存并直接进入数据库。如果您发现自己需要执行错过缓存的查询,则可能值得研究查询缓存。

    【讨论】:

    • 感谢您的回答。您写道:“那么下次您通过@Id 请求它时,它将从缓存中返回。”。我从不按 ID 列查询,我按 @UniqueConstraint 查询。它还会从缓存中返回吗? (equals 和 hashcode 也是基于 UniqueConstraint)
    • 我认为不会。据我了解,会话缓存本质上是 Id 到对象的大映射。因此,要从中获取实体,您必须使用 Id。
    猜你喜欢
    • 1970-01-01
    • 2011-03-07
    • 1970-01-01
    • 1970-01-01
    • 2013-01-18
    • 1970-01-01
    • 1970-01-01
    • 2015-12-18
    • 2015-10-23
    相关资源
    最近更新 更多