Избегайте n + 1 выбора с помощью кэшированных ассоциаций спящего режима или кэширующих коллекций в целом - PullRequest
4 голосов
/ 24 сентября 2010

У меня есть отношение один ко многим: родительская запись с 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>`

Я часто выбираю Родителя с помощью естественного ключа,а не первичный ключ, поэтому мне нужно включить кеширование запросов, чтобы воспользоваться кешем 2-го уровня (я использую ehcache).

Вот проблема: при извлечении родителяи получить попадание в кеш запроса, он становится запросом «выборка по первичному ключу».Это хорошо для «одного» конца моего «один ко многим».Если Родитель не найден в кэше, он выбирается из БД.Если мои n дочерних записей не найдены в кэше, Hibernate извлекает их, используя n последующих запросов select.N + 1 проблема выбора.

То, что я хочу - это способ кэшировать коллекцию дочерних объектов с ключом parent_id.Я хочу, чтобы Hibernate искал мою коллекцию в кеше в целом, а не как набор отдельных записей.Если коллекция не найдена, я хочу, чтобы Hibernate извлек коллекцию, используя 1 оператор select - fetch all Child with parent_id = x.

Это слишком много, чтобы спросить у Hibernate + ehcache?

1 Ответ

5 голосов
/ 25 сентября 2010

Я нашел свой ответ - можно настроить Hibernate + ehcache для выполнения того, что я описал выше.

Объявляя моего Ребенка как тип значения, а не как тип сущности (я полагаю, что это термины, используемые сообществом 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" 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>

Поведение моих дочерних объектов в этой конфигурации немного отличается от того, что было раньше - сейчас не определен отдельный первичный ключ для дочернего объекта, нет общих ссылок и пустых полей / столбцов. См. Документы Hibernate для более подробной информации об этом.

Мой Родитель и Ребенок доступны только для чтения, и я действительно хочу получить доступ к экземплярам Child только через Parent - я не использую Child независимо от Parent, поэтому обработка типа значения подходит для моего случая использования .

Большой победой для меня стало то, как коллекция кэшируется в моей новой конфигурации. Кэш коллекции теперь кэширует мою коллекцию в целом, используя ключ parent_id. Некоторые, но не все, из моей коллекции больше не могут находиться в кеше. Коллекция кэшируется и высвобождается в целом. Что еще более важно, если Hibernate отправляется на поиски моей коллекции в кэше 2-го уровня и получает ошибку, она выбирает всю коллекцию из БД одним запросом выбора.

Вот моя конфигурация 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>

Надеюсь, этот пример поможет кому-то еще.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...