7.11.2013

Hibernate cache второго уровня.

В одной из прошлых статей я расказывал про hibernate cache первого уровня, настало время расказать и про cache второго уровня. И так в крации напомню что кеш первого уровня встроен в hibernate, он всегда включен, его не возможно выключить. Область действия, в течении одной транзакции в сессии hibernate, т.е. между вызовами session.beginTransaction() и session.getTransaction().commit(). И так, зачем нам нужен hibernate cache второго уровня, прежде всего он уменьшает количество sql-запросов к базе данных, увеличивая при этом время отклика программы, что отражается на её быстродействии. Один раз попав в cache второго уровня  объект-сущность используется на всем протяжении жизни объекта sessionFactory, т. е. облать действия кеша - вся наша программа, а если быть точнее, то как вы настроете свой кеш, так он себя и поведет. В отличии от кеша первого уровня кеш  второго уровня нужно включать непосредственно в настройках Hibernate, и он реализуется second-level cache провайдером - сторонней библиотекой кешем. Выбор провайдера зависит от стратегии которые он поддерживает, под стратегией понимается что можно делать над объектом кеша: изменять, удалять, вставлять, читать, давайте рассмотрим их:
  1.  read-only - самая простая стратегия, кеш может только читаться, операции обновления (update) и удаления (delete) не разрешены, однако можно вставлять новые данные (insert) отлично подходит к кешированию различных справочников - наименование регионов, городов, улиц ... и т. д.
  2.  nonstrict read-write - данные этого кеша могут меняться, возможен конкурентный доступ к одному и тому же объекту.  Может произойти ситуация когда в кеше содержатся не последняя измененная сущность, т. е. данные сущности в кеше могут быть не равны данным в базе данных. Отсуда следует что нужно избегать конкурентного доступа, точнее одни и те же записи не должны редактировать 2 пользователя. Преведу пример: идеальный случаи это когда  кадровики редактируют только свои данные по работникам:  1-й кадровик с 1 по 200 таб. номер, 2-й кадровик с 201 по 400 таб. номер и т. д.
  3.  read-write - в целом похож на nonstrict read-write, позволяет более гибко настроить конкурентный доступ, поведение кеша зависит от настройки transaction isolation уровня базы данных, т. е. поведение изменения данных в кеше копирует поведение транзакции.  Максимум, чего можно выжать - это "repeatable read transaction isolation" уровень базы данных.
  4.  transactional - изменения в кеше и изменения в БД, полностью записываются в одной транзакции. У предыдущих двух стратегий была отложенная запись кеша, т. е. при изменении сущности, с начала происходит блокировка в кеше(soft locked), после применения транзакции с некоторым опазданием выполняется замена старого значения в кеше, новым. Уровнь этого кеша - это "serializable transaction isolation" уровень базы данных.
 Давайте я вам попробую объяснить, как происходит поиск сущности в кеше, с начала ищатся данные в кеше первого уровня, если нашли возвращаем, иначе ищется в кеше второго уровня, нашли возвращаем, иначе выполняем sql-запрос к БД. Стоит отметить что кеш первого уровня и второго уровня применим только к одиночным сущностям, т.е. когда запрашивается 1 объект,  если выполняется запрос нескольких сущностей, то нужно задействовать кеш запросов, который так же как и кеш второго уровня, нужно с начала включить в настройках hibernata. Хочу сказать пару слов о кеше запросов все что написано про стратегии кеша второго уровня справедливо и для кеша запросов.  Забегая вперед хочу что бы вы знали что такое регион кеша - это его логическое имя, он нужен для более тщательной и глубокой настройки кеша отдельно взятой сущности. И так приступим к настройке Ehcache:
  1. Включим кеш второго уровня в настройках Hibernate и подключим ehcache:
    <!-- Enable the second-level cache  -->
    <property name="cache.use_second_level_cache">true</property>
    <property name="cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
  2. Включим кеш запросов:
    <!-- Enable the query cache  -->
    <property name="cache.use_query_cache">true</property>
  3. Опищем сначала наши сущности (1), а потом какие сущности (2) должны, у нас кешироваться в кеше второго уровня, какую стратегию при этом применять,  поведение с lazy объектами, имя региона кеша:
    <!-- (1) Names the annotated entity class -->
    <mapping class="org.hibernate.tutorial.annotations.Event"/>
    <mapping class="org.hibernate.tutorial.annotations.Person"/>
    
    <!-- (2) Entity class the second-level cache -->
    <class-cache class="org.hibernate.tutorial.annotations.Person" usage="read-write" include="non-lazy"
                         region="org.hibernate.tutorial.annotations.Person1"/>
    <class-cache class="org.hibernate.tutorial.annotations.Event" usage="read-write" include="non-lazy"
                         region="org.hibernate.tutorial.annotations.Event1"/>
  4. Переходим к тонкой настройке сущностей кеша, конечно все можно настроить через аннотации но мне ближе старинный метод настройки через файл ehcache.xml:
    <?xml version="1.0" encoding="UTF-8"?>
    <ehcache name="Foo"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:noNamespaceSchemaLocation="ehcache.xsd">
        <!-- где будут у нас храниться данные на диске, в случае выгрузки
        кеша на диск -->
        <diskStore path="java.io.tmpdir"/>
        <!-- если вы не указали имя региона то по умолчанию используются параметры
         default кеша -->
        <defaultCache
                maxElementsInMemory="1000"
                eternal="false"
                timeToIdleSeconds="1200"
                timeToLiveSeconds="1200"
                overflowToDisk="true">
        </defaultCache>
        <!-- настраиваем наши регионы описанные раннее -->
        <cache name="org.hibernate.tutorial.annotations.Person1" maxElementsInMemory="1"
               eternal="false" timeToIdleSeconds="300" timeToLiveSeconds="600"
               overflowToDisk="true"
                />
        <cache name="org.hibernate.tutorial.annotations.Event1" maxElementsInMemory="1"
               eternal="false" timeToIdleSeconds="300" timeToLiveSeconds="600"
               overflowToDisk="true"
                />
    </ehcache>
  5. задействуем кеш запросов, его просто включить, вызовите метод setCacheable(true) и все сущности запроса попадут в кеш запросов:
    List result2 = session1.createQuery("from Person as p join fetch p.events").
                    setCacheable(true).list();
ну вот и все приведу на последок свой pom.xml с зависимостями:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>org.hibernate.tutorials</groupId>
    <artifactId>hibernate-tutorials</artifactId>
    <version>4.1.7.Final</version>
    <packaging>pom</packaging>
    <name>Hibernate Getting Started Guide Tutorials</name>
    <description>Aggregator for the Hibernate tutorials presented in the Getting Started Guide</description>

    <properties>
        <!-- Skip artifact deployment -->
        <maven.deploy.skip>true</maven.deploy.skip>
    </properties>

    <modules>
        <module>annotations</module>
        <module>annotations0</module>
        <module>annotations1</module>
        <module>annotations2</module>
        <module>annotations3</module>
        <module>annotations4</module>
        <module>annotations5</module>
        <module>annotations6</module>
        <module>annotations7</module>
        <module>annotations8</module>
    </modules>

    <dependencies>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>4.1.7.Final</version>
        </dependency>

        <!-- Hibernate uses jboss-logging for logging, for the tutorials we will use the sl4fj-simple backend -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.6.1</version>
        </dependency>

        <!-- The tutorials use JUnit test cases to illustrate usage -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
        </dependency>

        <!-- The tutorials use the H2 in-memory database -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.2.145</version>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-ehcache</artifactId>
            <version>4.1.7.Final</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-jdk14</artifactId>
            <version>1.6.6</version>
        </dependency>
        
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>0.11.0</version>
            <scope>provided</scope>
        </dependency>


        <!-- Hibernate c3p0 connection pool -->
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-c3p0</artifactId>
   <version>4.1.7.Final</version>
  </dependency>

        <!-- Hibernate jbosscache-core -->
        <dependency>
          <groupId>org.jboss.cache</groupId>
          <artifactId>jbosscache-core</artifactId>
          <version>3.2.5.GA</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
        </plugins>
        <testResources>
            <testResource>
                <filtering>false</filtering>
                <directory>src/test/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </testResource>
            <testResource>
                <directory>src/test/resources</directory>
            </testResource>
        </testResources>
    </build>

    <repositories>
        <repository>
            <id>sourceforge</id>
            <url>http://oss.sonatype.org/content/groups/sourceforge/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
        </repository>

        <repository>
            <id>terracotta-releases1</id>
            <url>http://www.terracotta.org/download/reflector/releases</url>
        </repository>

        <repository>
            <id>terracotta-releases</id>
            <url>http://www.terracotta.org/download/reflector/releases</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>

        <repository>
          <id>jboss-public-repository-group</id>
          <name>JBoss Public Maven Repository Group</name>
          <url>http://repository.jboss.org/nexus/content/groups/public-jboss/</url>
          <releases>
            <enabled>true</enabled>
          </releases>
          <snapshots>
            <enabled>true</enabled>
          </snapshots>
        </repository>
    </repositories>

</project>

Комментариев нет:

Отправить комментарий