sábado, 22 de octubre de 2011

Cache de Hibernate

Generalmente todos los cache se basan en almacenar objetos en memoria para acelerar accesos posteriores, pero debemos aclarar que en Hibernate hay dos tipos de cache: el Primer Nivel y el Segundo Nivel.

Cache de Primer Nivel

 Es el que mantiene automáticamente Hibernate cuando dentro de una transacción interactuamos con la base de datos, en éste caso se mantienen en memoria los objetos que fueron cargados y si mas adelante en el flujo del proceso volvemos a necesitarlos van a ser retornados desde el cache, ahorrando accesos sobre la base de datos. Lo podemos considerar como un cache de corta duración ya que es válido solamente entre el begin y el commit de una transacción, en forma aislada a las demás. Hibernate lo maneja por defecto, no hay que configurar nada, si por alguna razón queremos deshabilitar o evitar el uso del cache, podemos usar un tipo especial de session: StatelessSession, se obtiene de la sessionFactory con el método openStatelessSession(). Se usa en el caso de los procesos batch, por ejemplo cuando se tiene que hacer inserts o updates masivos, así se evita que cada vez que se hace el save de un objeto, el mismo se quede en memoria y en el correr del proceso se produzca un error del tipo OutOfMemoryError. La StatelessSession no interactúa con el Cache de Primer Nivel ni con el Cache de Segundo Nivel, es casi como si se utilizara JDBC directamente.

Cache de Segundo Nivel

El Cache de Segundo Nivel permite ir varios pasos mas adelante en la mejora de la performance. La diferencia fundamental es que éste tipo de cache es válido para todas las transacciones y puede persistir en memoria durante todo el tiempo en que el aplicativo esté online, lo podríamos considerar como un cache global. Para habilitar el Cache de Segundo Nivel hay que realizar lo siguiente:
1. Seleccionar un Proveedor de Cache. Por ejemplo EhCache.
2. Agregar en el hibernate.cfg.xml los siguientes properties:

 
 org.hibernate.cache.EhCacheProvider
 true
3. Poner en el classpath del aplicativo el archivo de configuración ehcache.xml, según las instrucciones del proveedor.
4. Agregar en el mapping de las clases que se queren seleccionar como "cacheables" la siguiente entrada:

Hibernate define tres niveles de cache que determinan el aislamiento:

  • transactional: Garantiza un nivel de aislamiento hasta repeatable read. Es el nivel más estricto. Solamente se puede utilizar en clusters, es decir, con cachés distribuidas. 'read-write: Mantiene un aislamiento hasta el nivel de commited.
  • nonstrict read-write: No ofrece garantía de consistencia entre el caché y la base de datos. Es una es'trategia ideal para almacenar datos que no cambian habitualmente y que no sean demasiado críticos.
  • read-only: Es la estrategia de concurrencia menos estricta. Recomendada para datos que nunca cambian.

Otro tema importante es definir qué entidades se van a "cachear", los candidatos naturales son por ejemplo las clases que representan: provincias, países, monedas o similares. Hay que tener en cuenta que cuando se tienen relaciones one-to-many hacia éstas entidades, se debe configurarlas como fetch=select, no como fetch=join porque si no Hibernate va a "levantar" la relación haciendo un join en lugar de intentar obtener el objeto desde el cache. Información general

Ehcache se integra fácilmente con la hibernate. Gavin King, el responsable del modo de hibernate, es también un confirmador al proyecto EHCache. Esto asegura EHCache seguirá siendo un caché de primer nivel para la hibernate. Configuración de EHCache para Hibernate es simple. Los pasos básicos son:

  • Descargar e instalar EHCache en su proyecto
  • Configurar EHCache como un proveedor de memoria caché en la configuración de su proyecto Hibernate.
  • Configurar almacenamiento en caché de segundo nivel en la configuración de su proyecto Hibernate.
  • Configuración de Hibernate de caché para cada entidad, o la consulta que desea almacenar en caché.
  • Configurar ehcache.xml como sea necesario para cada entidad, colección o consulta configurado para almacenar en caché.


Maven
Configurar o añadir el siguiente repositorio en el pom:


   terracotta-releases
   http://www.terracotta.org/download/reflector/releases
   true
   false

Configurar o añadir el módulo del núcleo ehcache definido por la siguiente dependencias (pom.xml):

   net.sf.ehcache
   ehcache-core
   ${ehcacheVersion}

Si está configurando Hibernate y EHCache con terracotta, agregar las siguientes dependencias para su construcción (pom.xml):

   net.sf.ehcache
   ehcache-terracotta
   ${ehcacheVersion}



   org.terracotta
   terracotta-toolkit-${toolkitAPIversion}-runtime
   ${toolkitVersion}

Configurar EHCache como el proveedor de caché de segundo nivel

Para configurar EHCache como caché de segundo nivel de Hibernate, se debe configurar hibernate de la siguiente manera el archivo hibernate.cfg.xml:
Agregar la siguiente propiedad:

         net.sf.ehcache.hibernate.EhCacheRegionFactory
Para la creación de la instancia,

         net.sf.ehcache.hibernate.SingletonEhCacheRegionFactory
Para forzar a Hibernate a utilizar un singleton de EHCache CacheManager. Hibernate 3,0 a 3,2 uso:
         
         net.sf.ehcache.hibernate.EhCacheProvider
  
Para la creación de la instancia, o

         net.sf.ehcache.hibernate.SingletonEhCacheProvider

Para forzar a Hibernate a utilizar un singleton EHCache CacheManager.
        
         net.sf.ehcache.hibernate.SingletonEhCacheProvider
  

Habilitar caché de segundo nivel y la configuración de la caché de consultas
         
         net.sf.ehcache.hibernate.EhCacheProvider
  

Además de configurar la configuración de caché de segundo nivel de proveedores, usted tendrá que activar el caché de segundo nivel (por defecto está configurado en off - "falso" - por Hibernate). Esto se hace mediante el establecimiento de la siguiente propiedad en la configuración de su hibernación:

true
También es posible que desee activar la caché de consultas de Hibernate. Esto se hace mediante el establecimiento de la siguiente propiedad en la configuración de su hibernate config:

true

Configurar Hibernate Entidades de utilizar Segundo caché a nivel

Además de la configuración de la hibernación para uso de la caché de segundo nivel, Hibernate también se debe activar la caché de entidades, colecciones y consultas. Por ejemplo, para habilitar las entradas de caché para el com.somecompany.someproject.domain.Country se debe agregar lo siguiente al mapeo de hibernate:


...


Para habilitar el almacenamiento en caché, agregue el siguiente elemento.

Donde read-write|nonstrict-read-write|read-only es el tipo de cache.
Por ejemplo:


 
...



Esto también puede lograrse mediante la anotación @ caché, por ejemplo,

@Entity @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Country { ... }

Definición de las estrategias de caché diferente

De sólo lectura: Cachés de datos que no se actualiza.

No estricta-lectura-escritura:Cachés de datos que a veces se actualiza sin bloqueo de la memoria caché. Si el acceso simultáneo a un elemento es posible, esta estrategia de concurrencia no garantiza que el producto devuelto de la caché es la última versión disponible en la base de datos. Configurar el tiempo de espera de caché en consecuencia.

De lectura y escritura: Cachés de datos que a veces se actualiza la vez que mantiene la semántica de "lectura confirmada" nivel de aislamiento. Si la base de datos está establecida en "lectura repetible", esta estrategia de concurrencia casi mantiene la semántica. De aislamiento de lectura repetible se ve comprometida en el caso de escrituras simultáneas.


Query Cache

Para mejorar la performance y rendimiento de las aplicaciones podemos utilizar Query Cache.
Si lo que interesa es "cachear" el resultado exacto de una consulta, no objetos individuales. Por ejemplo, si tenemos un método en un DAO que retorna la lista de Países registrados en la base de datos, es muy probable que siempre retorne el mismo resultado ya que esa tabla no cambia a menudo, entonces es recomendable establecer la consulta como "cacheable".

Hay que tener en cuenta que el query cache solo almacena los identificadores de los objetos del resultado, es decir que lo debemos usar combinado con el Cache de Segundo Nivel. Los pasos serían:

1. Establecer el property hibernate.cache.use_query_cache=true en el hibernate.cfg.xml
2. Configurar la entidad Pais como "cacheable"
3. Establecer la consulta como "cacheable":



List paises = sess.createQuery("from Pais") .setCacheable(true) .list();


Por lo tanto, la primera vez que se ejecuta la consulta, se retorna la lista de Paises desde la tabla mediante un select, pero a partir de ese momento toda vez que se repita el query, el resultado va a ser retornado desde el cache, evitando la comunicación con la base de datos.

¿Que pasa si agrego un nuevo Pais?

Entonces el query dejaría de ser válido, solo si agregamos un nuevo Pais pasando por la session de Hibernate, el query se invalida automáticamente para que la próxima vez que se ejecute la consulta vuelva a obtener el resultado desde la base de datos. Es importante aclarar que el comportamiento anterior no se cumple si se inserta un Pais por afuera del aplicativo, es decir con un insert directo a la tabla, o si se tiene dos aplicativos separados que apuntan a la misma base de datos, en ese caso habría que usar JNDI para que todos compartan la misma SessionFactory.

Es recomendable usar el query cache solo en los casos en que una consulta se repite constantemente y la entidad resultado no cambia frecuentemente, por ejemplo: Países, Provincias, etc.