避免n + 1选择与缓存Hibernate关联或缓存集合作为一个整体

bse*_*ans 4 collections caching hibernate

我有一对多关系:父记录与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>`
Run Code Online (Sandbox Code Playgroud)

我经常一个自然键,而不是主键获取父,所以我需要启用查询缓存,以便采取的二级高速缓存(我用的Ehcache)的优势.

这是问题所在:当我获取父级并在查询缓存中获得命中时,它将成为"按主键提取"查询.对于我的一对多的"一"结尾来说这很好.如果在缓存中找不到Parent,则从DB中获取它.如果在缓存中找不到我的n个子记录,Hibernate会使用n个后续选择查询来获取它们.N + 1选择问题.

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

从Hibernate + ehcache这个问题太多了吗?

bse*_*ans 5

我找到了自己的答案 - 可以配置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>
Run Code Online (Sandbox Code Playgroud)

在此配置下,我的Child对象的行为与以前相比有点不同 - 现在没有为Child定义单独的主键,没有共享引用,也没有可空字段/列.有关详细信息,请参阅Hibernate文档.

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

对我来说最大的胜利是如何在我的新配置下缓存该集合.集合缓存现在将我的集合整体缓存,由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>
Run Code Online (Sandbox Code Playgroud)

希望这个例子可以帮助别人.