Спящий режим загружает все объекты, используя кеш 1-го или 2-го уровня.

У нас есть целая таблица сущностей, которые нам нужно загрузить во время сеанса гибернации, и единственный известный мне способ загрузить все сущности — через запрос HQL:

public <T> List<T> getAllEntities(final Class<T> entityClass) {
    if (null == entityClass)
        throw new IllegalArgumentException("entityClass can't be null");

    List<T> list = castResultList(createQuery(
            "select e from " + entityClass.getSimpleName() + " e ").list());


    return list;
}

Мы используем EHcache для кэширования 2-го уровня.

Проблема в том, что это вызывается 100 раз в данном сеансе транзакции и занимает значительную часть общего времени. Есть ли способ загрузить все объекты данного типа (загрузить всю таблицу) и при этом получить выгоду от кэша сеанса 1-го уровня или ehcache 2-го уровня.

Нам сказали держаться подальше от кэширования запросов из-за их потенциального снижения производительности по сравнению с их преимуществами. * кэш запросов Hibernate считается опасным

Хотя сейчас мы занимаемся профилированием производительности, возможно, пора попробовать включить кеширование запросов.


person Dougnukem    schedule 31.08.2010    source источник
comment
Почему ваш код перезагружает всю таблицу сотни раз в одной и той же транзакции? Если это связано с тем, что другие потоки могут выполнять вставки, то ни кеш L1, ни L2 не смогут вам помочь, даже если вы перепишете код каким-то тайным способом, чтобы использовать их.   -  person Affe    schedule 01.09.2010
comment
Это статическая таблица (по крайней мере, статическая каждые 24 часа), более того, метод вызывается 100 раз в данной транзакции, мы могли бы легко кэшировать этот метод для этой транзакции в нашем DAO, но мне было любопытно, изобретаю ли я кэширование заново это уже было сделано, если бы я загрузил таблицу по-другому.   -  person Dougnukem    schedule 01.09.2010


Ответы (3)


Кэш L1 и L2 не может помочь вам с проблемой «получить всю таблицу».

Кэш L1 плохо оснащен, потому что если кто-то еще что-то вставил, то его там нет. (Вы можете «знать», что никто другой никогда не сделает этого в рамках бизнес-правил системы, но сеанс Hibernate не знает.) Следовательно, вам нужно заглянуть в БД, чтобы быть уверенным.

Что касается кеша L2, срок его действия может быть истёк или сброшен с тех пор, как кто-то в последний раз помещал туда таблицу. Это может быть отдано на милость поставщику кэша или даже полностью извне, например, через MBean. Таким образом, Hibernate не может знать в любой момент времени, представляет ли то, что находится в кеше для этого типа, все содержимое таблицы. Опять же, вам нужно заглянуть в БД, чтобы убедиться.

Поскольку у вас есть специальные знания об этой сущности (новые никогда не создаются), которые невозможно передать в кэши L1 или L2, вам нужно либо использовать инструмент, предоставляемый Hibernate, когда у вас есть специальные бизнес-правила знания о наборе результатов, кэшировании запросов или кэшировании информации самостоятельно.

--

Если вы действительно хотите, чтобы это было в кеше L2, теоретически вы могли бы сделать все объекты в таблице членами коллекции на каком-то другом фиктивном объекте, а затем включить кэширование коллекции и тайно управлять ею в DAO. Я не думаю, что это может стоить того, чтобы в вашем коде была такая странность :)

person Affe    schedule 01.09.2010

Кэш запросов считается вредным тогда и только тогда, когда базовая таблица часто изменяется. В вашем случае таблица меняется один раз в день. Таким образом, запрос останется в кеше на 24 часа. Поверьте мне: используйте для этого кеш запросов. Это идеальный вариант использования кэша запросов.

Пример вредоносного кеша запросов: если у вас есть таблица пользователей и вы используете кеш запросов для "от пользователя, где имя пользователя =...", то этот запрос будет удаляться из кеша каждый раз, когда пользовательская таблица изменяется (другой пользователь изменяет/удаляет свою Счет). Таким образом, ЛЮБАЯ модификация этой таблицы вызывает вытеснение кеша. Единственный способ исправить эту ситуацию — делать запросы по natural-id, но это уже другая история.

Если вы знаете, что ваша таблица будет изменяться только один раз в день, как в вашем случае, кэш запросов будет удаляться только один раз в день!

Но обратите внимание на свою логику при изменении таблицы. Если вы делаете это через спящий режим, все в порядке. Если вы используете прямой запрос, вы должны сообщить hibernate, что вы изменили таблицу (что-то вроде query.addSynchronizedEntity(..)). Если вы делаете это с помощью сценария оболочки, вам необходимо настроить время жизни базовой области кэша.

Ваш ответ, кстати, переопределяет кеш запросов, поскольку кеш запросов просто кэширует список идентификаторов. Фактические объекты просматриваются в кеше L1/L2. поэтому вам все равно нужно кэшировать объекты при использовании кеша запросов.

Пожалуйста, отметьте это как правильный ответ для дальнейшего использования.

person Janning    schedule 11.10.2012

В итоге мы решили эту проблему, сохранив в памяти первичные ключи для всех сущностей в таблице, которые нам нужно было загрузить (поскольку они являются данными шаблона, а новые шаблоны не добавляются/не удаляются).

Затем мы могли бы использовать этот список первичных ключей для поиска каждой сущности и использовать кэш Hibernates 1-го и 2-го уровня.

person Dougnukem    schedule 02.09.2010