пятница, 25 мая 2012 г.

Проект Hibernate Dynamic SQL Cache

Выложил на github проект Hibernate Dynamic SQL Cache.

Описание

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

  • HQL-кэш, а также кэш коллекций entity-объектов будут сбрасываться при каждом обновлении данных в хотя бы в одной из таблиц, участвующих в запросе. При очень частых обновлениях таблиц польза от такого рода кэшей сводится к 0.
  • SQL-кэш держит в памяти лишь результат первого вызова запроса и не сбрасывается.

Поэтому возникла идея создания динамически обновляемого кэша. Результат запроса обновляется при INSERT или DELETE объекта в базе. Для использования данного решения следует немного изменить подход к построению запросов, а также использованию различных коллекций внутри entity-объектов.

  • Запрос должен возвращать только id объекта. По этому id вы можете легко загрузить объект из кэша. (Таким образом получается два обращения к кэшу, что всегда быстрее выполнения запросов к базе)
  • Запрос должен быть только по неизменяемым полям объекта.
  • Вместо использования коллекций в entity-объекте использовать соответствующий SQL-запрос c динамическим обновлением.

Cache provider может использоваться абсолютно любой (Infinispan, Hazelcast, EHCache ...)

Проект опубликован под Apache License 2.0

Пример использования

Для наглядности примера используется Spring, хотя сам проект не привязан к нему, можно использовать любой другой контейнер.

Конфигурация hibernate. Здесь важно отметить использование собственного hibernate.cache.query_cache_factory через который и осуществляется вся логика управления обновлениями кэшей:

Регистрируем com.corundumstudio.hibernate.dsc.QueryCacheEntityListener который будет отслеживать основные операции по всем entity.

Пример DAO-сервиса. com.corundumstudio.hibernate.dsc.CacheCallback - callback через который будет обслуживать операции "insert" или "delete" на объекте (в данном примере SimpleEntity), чтобы результаты запроса были всегда "свежими".

Теперь при вызове SimpleEntityDao.getEntityByPhone, первый раз будет произведено обращение к БД. Последующие вызовы метода будут возвращать значение из кэша, причем если объект SimpleEntity с искомым значением phone будет добавлен в базу он также появится и в результате данного запроса. И наоборот, если объект с таким значением phone будет удален, то результат запроса будет возвращать null

Если результат запроса возвращает список объектов, то в этом случае, при удалении/создании объекта, в списке будет удаляться/добавляться его id