hibernate - 避免使用缓存 Hibernate 关联或缓存集合作为整体进行 n+1 选择

标签 hibernate collections caching

我有一对多关系:父记录与 n 子记录。这些记录经常使用且只读,非常适合缓存。

这是我的 Hibernate 映射的近似值:

`<class name="Parent" table="Parent>
   <cache usage="read-only"/>
   <id name="primary_key"/>
   <property name="natural_key"/>

   <set name="children" lazy="false" fetch="join">
      <cache usage="read-only"/>
      <key-column name="parent_id"/>
      <one-to-many class="Child"/>
   </set>
</class>

<class name="Child" table="Child">
   <cache usage="read-only"/>
   <id name="primary_key"/>
   <property name="parent_id"/>
</class>`

我经常通过自然键而不是主键获取父级,因此我需要启用查询缓存才能利用二级缓存(我使用ehcache)。

问题如下:当我获取父项并在查询缓存中命中时,它变成“按主键获取”查询。这对于我的一对多的“一”端来说很好。如果在缓存中找不到父级,则从数据库中获取。如果在缓存中找不到我的 n 个子记录,Hibernate 将使用 n 个后续选择查询来获取它们。 N+1选择问题。

我想要的是一种缓存子对象集合的方法,以parent_id为键。我希望 Hibernate 在缓存中作为一个整体来查找我的集合,而不是作为一堆单独的记录。如果找不到集合,我希望 Hibernate 使用 1 个 select 语句来获取集合 - 获取所有带有parent_id=x 的子项。

对 Hibernate + ehcache 的要求是否过高?

最佳答案

我找到了自己的答案 - 可以配置 Hibernate + ehcache 来执行我上面描述的操作。

通过将我的 Child 声明为值类型而不是实体类型(我相信这些是 Hibernate 社区使用的术语),我基本上可以将我的 Child 视为 Parent 的组件而不是单独的实体。这是我修改后的映射的示例:

<class name="Parent" table="Parent">
   <cache usage="read-only"/>
   <id name="primary_key"/>
   <property name="natural_key"/>

   <set name="children" lazy="false" fetch="join" table="Child">
      <cache usage="read-only"/>
      <key-column name="parent_id"/>
      <composite-element class="Child">
         <property name="property1" column="PROP1" type="string">
         <property name="property2" column="PROP2" type="string">
      </composite-element>
   </set>
</class>

在此配置下,我的 Child 对象的行为与以前略有不同 - 现在没有为 Child 定义单独的主键,没有共享引用,也没有可为空的字段/列。请参阅Hibernate docs了解更多详细信息。

我的父级和子级都是只读的,我真的只想通过父级访问子级的实例 - 我不会独立于父级使用子级,因此值类型处理非常适合我的用例.

对我来说最大的胜利是集合在我的新配置下的缓存方式。集合缓存现在将我的集合作为一个整体进行缓存,以parent_id 为键。我的收藏中的部分(但不是全部)不再可能位于缓存中。该集合作为一个整体被缓存和驱逐。更重要的是,如果 Hibernate 在二级缓存中查找我的集合但未命中,它会通过单个选择查询从数据库中获取整个集合。

这是我的 ehcache 配置:

 <ehcache>
    <cache name="query.Parent"
        maxElementsInMemory="10"
        eternal="false"
        overflowToDisk="false"
        timeToIdleSeconds="0"
        timeToLiveSeconds="43200" 
    </cache>
    <cache name="Parent"
        maxElementsInMemory="10"
        eternal="false"
        overflowToDisk="false"
        timeToIdleSeconds="0"
        timeToLiveSeconds="43200" 
    </cache>
    <cache name="Parent.children"
        maxElementsInMemory="10"
        eternal="false"
        overflowToDisk="false"
        timeToIdleSeconds="0"
        timeToLiveSeconds="43200" 
    </cache>
<ehcache>

希望这个例子对其他人有帮助。

关于hibernate - 避免使用缓存 Hibernate 关联或缓存集合作为整体进行 n+1 选择,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3783681/

相关文章:

java - @Column(nullable=false) 在应用程序级别验证空检查

java - 如何为 hibernate > 4.3 导出模式

java - JPA如何推断属性的数据类型

javascript - 如何设置collectionsjs等于并比较?

ruby-on-rails - Google Geocoding API 错误 : over query limit. - Rails

c++ - 测量 ARM Cortex-A15 的缓存访问时间/周期

java - 覆盖@JoinColumn 可为空的值

algorithm - 负载系数0.75是什么意思?

c# - 将 List<int> 转换为 List<List<int>>

Python lru_cache : how can currsize < misses < maxsize?