我有一对多关系:包含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>`
我经常通过自然键而不是主键来获取Parent,所以我需要启用查询缓存才能利用二级缓存(我使用ehcache)。
这是问题:当我获取父级并在查询缓存中获得命中时,它变为“按主要提取”查询。对于我的一对多的“一”结尾来说这很好。如果在缓存中找不到Parent,则从DB中获取它。如果在缓存中找不到我的n个子记录,Hibernate会使用n个后续选择查询来获取它们。 N + 1选择问题。
我想要的是是一种缓存由parent_id键入的Child对象集合的方法。我希望Hibernate在整个缓存中查找我的集合,而不是作为一堆单独的记录。如果找不到集合,我希望Hibernate使用1 select语句获取集合 - 使用parent_id = x获取所有Child。
从Hibernate + ehcache那里问这个太多了吗?
答案 0 :(得分: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>
我的Child对象的行为在此配置下与以前略有不同 - 现在没有为Child定义单独的主键,没有共享引用,也没有可空字段/列。有关详细信息,请参阅Hibernate docs。
我的父母和孩子都是只读的,我真的只想通过父母访问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>
希望这个例子可以帮助别人。