Кешування другого рівня в Hibernate.

Частина II: Бібліотеки кешування для Hibernate

Кеш другого рівня в Hibernate може бути підключеним і може бути в межах процесу або кластера. Можуть бути різні реалізації кешу другого рівня, існують кілька вже готових реалізацій на основі готових движків кешування. Але можна реалізувати власний движок і підключити його за допомогою реалізації інтерфейсу org.hibernate.cache.CacheProvider.

Визначають наступні провайдери кешу другого рівня

- EHCache. Підтримка кешування другого рівня в межах одного процесу однієї віртуальної машини. Може виконувати зберігання кешу як в оперативній памяті, так і на диск. Підтримує кешування запитів.

- OSCache (OpenSymphony Cache). Підтримка кешування другого рівня в межах одного процесу однієї віртуальної машини. Підтримує кешування запитів.

- SwarmCache. Підтримка кешування другого рівня в межах кластера. Не підтримує кешування запитів.

- JBossCache. Підтримка кешування другого рівня в межах кластера. Підтримує кешування запитів.

Про підтримку стратегій кешування різними провайдерами можна дізнатися із наступної таблиці:

Провайдер

Read-only

Read-write

Nonstrict-read-write

Transactional

EHCache

+

+

+

OSCache

+

+

+

SwarmCache

+

+

JBossCache

+

+

Підтримка кешування другого рівня в Hibernate

Кешування другого рівня в Hibernate потрібно увімкнути, для того, щоб його використовувати. Увімкнути кешування другого рівня можна встановши property в конфігурації сесії Hibernate:

<property name="cache.use_second_level_cache">true</property>

Якщо необхідно увімкнути також кешування для запитів, то слід також встановити значення для property в конфігурації сесії Hibernate:

<property name="hibernate.cache.use_query_cache">true</property>

Також необхідно вказати клас провайдера кешування, а для цього треба задати наступний property в конфігурації сесії Hibernate :

- для провайдера EHCache

<property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>

- для провайдера OSCache

<property name="cache.provider_class">org.hibernate.cache.OSCacheProvider</property>

- для провайдера SwarmCache

<property name="cache.provider_class">org.hibernate.cache.SwarmCacheProvider</property>

Але для того, щоб кешування другого рівня було в дії, слід позначити хоча б один клас для кешування. Позначити певний клас для кешування можна двома способами:

  1. В описі маппінгу класу у файлі *.hbm.xml добавити елемент cache із відповідним значенням.

<class name=”org.prisoft.sample.hibernate.Country” table=”Country” mutable="false">

<cache usage="read-only"/>

</class>

Вище описаний клас Country, який мапиться на таблицю Country. Ця таблиця є словником країн. Словник зміні не підлягає, тобто значення не добавляється, не редагується та не видаляється. <cache> вказує на те, що дані підлягають кешуванню, стратегія кешування read-only.

  1. У файлі конфігурації сесії hibernate.cfg.xml. Для цього використовується елемент <class-cache>, який наводиться після списку ресурсів. Ось як може описуватися кешування для класу Country:

<hibernate-configuration>

<session-factory>

….

<mapping resource=”org/prisoft/sample/hibernate/Country.hbm.xml”/>

….

<class-cache class=”org.prisoft.sample.hibernate.Country”

usage=”read-only”/>

</session-factory>

</hibernate-configuration>

Я надаю перевагу конфігурації кешування другого рівня через файл hibernate.cfg.xml, оскільки так видно повний список класів, що підлягають кешуванню, а також при необхідності, можна легко відключити кешування.

Отже, ми розглянули, як включити кешування другого рівня, а також як вказати які саме ентіті підлягають кешуванню. Ми не розглянули, як задається кешування для запитів.

Також ми не розглянули таке поняття, як регіони. Регіони – це так би мовити іменовані екземпляри кешу, в якому знаходяться різні класи із різними політиками часу на існування. По замовчуванню, ім’я регіону складає повна назва класу (якщо це кеш класу) або повна назва класу плюс імя property класу (якщо це колекція). За допомогою property сесії Hibernate hibernate.cache.region_prefix. Тоді ім’я регіону буде складатися із префіксу та повного імені класу. Вказати ім’я регіону можна явно за допомогою атрибута region в елементі <cache> або <class-cache>.

EHCache (http://ehcache.sourceforge.net)

Останнє обновлення аплікації це є ehcache-1.4-beta, яка вийшла 1 січня 2008. А це означає, що продукт не тільки живий, але і розвивається. Розповсюджується EhCache за ліцензією The Apache Software License, Version 2.0. Спонсори там також далеко не з послідніх. Так що, проект живе і розвивається. Не може не радувати повна підтримка JSR107 (JCache API).

Серед вказаних характеристик слід відзначити:

- Простота (API є досить простим, початкова конфігурація також не є необхідною).

- Залежність тільки від commons-logging та commons-collections. Вони в свою чергу також поширюються за APL. Та й більшість проектів їх використовує. То ж, ця залежність не є проблематичною.

- Кешування в оперативній пам’яті та на диск. Підтримка великих розмірів.

- Підтримка великої кількості кешів.

- Підтримка розширень для кешу (а також розширення для загружчиків та обробників виключень).

- А також багато чого іншого, про що ви можете точніше дізнатися за адресом http://ehcache.sourceforge.net/features.html

Як уже зазначено, можна і не конфігурувати EHCache. Але я не думаю, що це хороша ідея. Нам вона явно не підходить. Ми повинні навчитися конфігурувати EhCache.

Отже, конфігурація EHCache знаходиться у файлі ehcache.xml, який у свою чергу знаходиться в класспасі аплікації.

Отже, структура конфігураційного файла:

<ehcache>

<diskStore …/>

<defaultCache …/>

<cache name=”...” …/>

</ehcache>

Конфігураційний файл може містити не тільки ці елементи, але й інші. Але вони розглядатися не будуть.

Роглянемо детальніше ці елементи.

<diskStore path=”java.io.tmpdir”/> - вказує шлях до каталогу, де файли кешу створюються.

Наступні значення є можливими:

- user.homeдомашній каталог користувача;

- user.dirтекучий робочий каталог користувача;

- java.io.tmpdir – каталог тимчасових файлів.

Можна також вказувати субдиректорії, наприклад <diskStore path=”java.io.tmpdir/cache/myapp”/>

Конфігурація кешу, який не описаний за допомогою власного <cache> елемента, відбувається через <defaultCache>.

<defaultCache

maxElementsInMemory=”10000”

eternal=”false”

timeToIdleSeconds=”120”

timeToLiveSeconds=”120”

overflowToDisk=”true”

diskSpoolBufferSizeMB=”30”

maxElementsOnDisk=”1000000”

diskPersistent=”false”

diskExpiryThreadInternalSeconds=”120”

memoryStoreEvictionPolicy=”LRU”/>

Можна також задавати конфігурацію для окремого кешу; а для цього використовується наступний елемент

<сache name=”CacheName”

maxElementsInMemory=”10000”

eternal=”false”

timeToIdleSeconds=”120”

timeToLiveSeconds=”120”

overflowToDisk=”true”

maxElementsOnDisk=”1000000”

diskPersistent=”false”

diskExpiryThreadInternalSeconds=”120”

memoryStoreEvictionPolicy=”LRU”/>

А тепер детальніше про кожен атрибут.

mameназва кешу; назва, як ви вже, мабуть, зрозуміли має бути унікальна.

maxElementsInMemory – максимальна кількість об’єктів, які будуть створені та зберігатися в кеші в оперативній пам’яті.

maxElementsOnDisk – максимальна кількість об’єктів, які будуть збережені на диск.

eternalвказує на те, чи є елементи вічними. Якщо вони є вічними, то будь-які настройки Idle та Live Seconds ігноруються, елементи зберігаються до кінця.

timeToIdleSeconds час життя невикористаного елемента кешу (за умови, якщо eternal рівний false). Якщо значення 0, то це значить, що елемент може залишатися в кеші в стані не використовуваного нескінченний період.

timeToLiveSeconds – час життя елемента кешу (за умови, якщо eternal рівний false). Якщо значення 0, то це значить, що елемент може залишатися в кеші в стані невикористаного нескінченний період.

overflowToDisk – вказує на те, чи зберігати елементи на диск, якщо досягнуто максимуму елементів в пам’яті.

diskPersistent - чи зберігаються закешовані дані між пере запуском віртуальної машини. Значення по-замовчуванню false.

diskExpiryThreadInternalSeconds – кількість секунд, через які відбувається запуск потоку очищення диску від елементів кешу, що завершили свій час життя.

memoryStoreEvectionPolicy. Розрізняються наступні політики LRULeast Recently Used, FIFO – First In First Out, LFU Last Frequently Used.

Приклад аплікації із використанням кешу EHCache

Отже, розглянуто трохи теорії, а тепер ближче до практики. Для цього було створено просту аплікацію, яка містить 4 доменних об’єкта: Country, Materials, Product, ProductMaterial.

Отже, ми маємо аплікацію, яка містить інформацію про матеріали (назва, код, ціна, країна виробник), про продукцію (назва, код, ціна, країна виробник, матеріали із яких виготовлена продукція) і власне словник країн виробників.

Кешування класів та стратегії кешування описані у файлі hibernate.cfg.xml.

Отже, hibernate.cfg.xml має вигляд:

<hibernate-configuration>

<session-factory>

<property name="hibernate.connection.url">jdbc:mysql://localhost/proregister</property>

<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>

<property name="hibernate.connection.username">root</property>

<property name="hibernate.connection.password"/>

<property name="hibernate.show_sql">false</property>

<mapping resource="org/prisoft/sample/proregister/objects/Country.hbm.xml"/>

<mapping resource="org/prisoft/sample/proregister/objects/Material.hbm.xml"/>

<mapping resource="org/prisoft/sample/proregister/objects/Product.hbm.xml"/>

<mapping resource="org/prisoft/sample/proregister/objects/ProductMaterial.hbm.xml"/>

<class-cache class="org.prisoft.sample.proregister.objects.Country" usage="read-only"/>

<class-cache class="org.prisoft.sample.proregister.objects.Material" usage="read-write"/>

<class-cache class="org.prisoft.sample.proregister.objects.Product" usage="read-write"/>

<class-cache class="org.prisoft.sample.proregister.objects.ProductMaterial" usage="read-write"/>

</session-factory>

</hibernate-configuration>

Клас Country має стратегію кешування read-only, в той час як всім іншим задана стратегія read-write. Country є по факту незмінним словником країн; його не можна змінювати.

Кеши класів Material, Product та ProductMaterial повинні підтримувати зміну, оскільки елементи можуть як добавлятися, так і редагуватися та видалятися. Саме тому їм задана стратегія read-write.

Аплікація працюватиме із базою даних на одній машині, тому виконувати кешування на диск немає потреби. Окрім того, кількість даних є невеликою, тому їх можна спокійно зберігати в базу даних.

Для детальної конфігурації EHCache потрібно конфігурувати кожен кеш у файлі ehcache.xml.

Отже, конфігурація має наступний вигляд:

<ehcache>

<diskStore path="java.io.tmpdir"/>

<defaultCache

maxElementsInMemory="10000"

eternal="false"

timeToIdleSeconds="120"

timeToLiveSeconds="120"

overflowToDisk="false"

diskPersistent="false"

diskExpiryThreadIntervalSeconds="120"

memoryStoreEvictionPolicy="LRU"/>

[тут має бути конфігурація кешів, розглянута дальше]

</ehcache>

Отже, ми вказали на каталог для файлів кешування та конфігурацію кешу по-замовчуванню.

Нижче подана конфігурація кешу для класу Country. Задаємо максимальну кількість елементів в пам’яті, забороняється записувати дані на диск, дані можуть зберігатися тривалий час. Якщо кількість елементів в пам’яті перевищує 200, то тоді відбувається очистка кешу.

<cache name="org.prisoft.sample.proregister.objects.Country "

maxElementsInMemory="200"

eternal="true"

overflowToDisk="false"/>

Настройка кешу для об’єктів Product, Material та ProductMaterial має відмінності, оскільки ці об’єкти можуть змінюватися, тому для них задана конфігурація тривалості існування елементів кешу. Конфігурація подана нижче.

<cache name=”org.prisoft.sample.proregister.Product”

maxElemenentsInMemory=”2000”

eternal=”false”

overflowToDisk=”false”

timeToIdleSeconds=”120”

timeToLiveSeconds=”300”/>

<cache name=”org.prisoft.sample.proregister.Material”

maxElemenentsInMemory=”5000”

eternal=”false”

overflowToDisk=”false”

timeToIdleSeconds=”240”

timeToLiveSeconds=”300”/>

<cache name=”org.prisoft.sample.proregister.ProductMaterial”

maxElemenentsInMemory=”10000”

eternal=”false”

overflowToDisk=”false”

timeToIdleSeconds=”120”

timeToLiveSeconds=”120”/>

Як бачите, для різних кешів задана різна кількість елементів в памяті. Атрибут eternal має значення false, тому конфігурація періоду існування елементів у кеші не буде ігноруватися. Дані не зберігаються на диск для жодного із кешів, використовуваних в аплікації.

Отже, ми розглянули які існують провайдери кешу другого рівня для Hibernate, також ми розглянули, як настроїти Hibernate для використання кешу другого рівня для класів (проте не зачепили теми кешування запитів та колекцій). Розглянуто було також більш детально провайдер EHCache.

Додаткова література

1. Hibernate In Action, Manning Publications, 2005

2. EhCache User Guide at http://ehcache.sourceforge.net/EhcacheUserGuide.html