Caché de segundo nivel en Hibernate con Ehcache

Introducción al caché de segundo nivel

Propósito del mecanismo de caché:

El caché acelera la recuperación de grandes volúmenes de datos, mejorando significativamente el rendimiento de la aplicación.

Criterios para almacanar datos en caché:

Resulta apropiado para datos que se modifican con poca frecuencia o que permanecen estáticos, como catálogos o diccionarios de datos. Aplica también para operaciones de consultas costosas, como análisis estadísticos o facturación telefónica.

Clasificación de bases de datos:

Las bases de datos relacionales (MySQL, Oracle, SQL Server) mantienen vínculos entre entidades. En contraste, las no relacionales operan con estructuras desacopladas basadas en clave-valor.

Ejemplos de sistemas no relacionales:

  • Almacenamiento en archivos: Ehcache
  • Almacenamiento en memoria: Redis, Memcache
  • Basado en documentos: MongoDB

Ehcache: marco de caché en memoria

Ehcache es un framework de caché robusto para Java, reconocido por su simplicidad de configuración y alto rendimiento. Para comprender su funcionamiento básico, veamos una implementación con Map:

package com.ht.cache.demo;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class CacheSimulator {
    private static final Map<String, Object> store = new ConcurrentHashMap<>();

    public static Object fetchData(String queryKey) {
        Object cachedData = store.get(queryKey);
        System.out.println("Intentando recuperar del caché...");
        if (cachedData == null) {
            System.out.println("Cargando desde fuente original...");
            store.put(queryKey, new Object[]{"datos_simulados"});
            return store.get(queryKey);
        }
        return cachedData;
    }

    public static void main(String[] arguments) {
        System.out.println(fetchData("consulta_1"));
        System.out.println(fetchData("consulta_1"));
    }
}

Este ejemplo muestra el principio básico: almacena resultados en memoria para evitar consultas repetidas.

Características principales de Ehcache:

  • Alto rendimiento: Diseñado para sistemas con alta concurrencia
  • Integración sencilla: API intuitiva con mínima configuración requerida
  • Ligero: Tamaño reducido (menos de 2MB) y dependencia mínima
  • Escalable: Soporta algoritmos de eviction como LRU, LFU y FIFO
  • Capacidad distribuida: Disponible desde la versión 1.2

Interfaces fundamentales:

  1. CacheManager: Administra instancias de caché
  2. Cache: Contenedor de elementos almacenados
  3. Element: Unidad individual de datos en caché

Configuración e implementación

Primero incluimos las dependencias en el archivo POM:

<properties>
    <hibernate.version>5.2.12.Final</hibernate.version>
    <ehcache.version>2.10.0</ehcache.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-ehcache</artifactId>
        <version>${hibernate.version}</version>
    </dependency>
</dependencies>

Archivo de configuración Ehcache

<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
    <diskStore path="java.io.tmpdir"/>
    
    <defaultCache 
        maxElementsInMemory="1000"
        eternal="false"
        timeToLiveSeconds="600"
        memoryStoreEvictionPolicy="LRU"/>
        
    <cache name="com.entities.User"
           maxElementsInMemory="200"
           timeToIdleSeconds="300"/>
</ehcache>

Clase utilitaria para operaciones de caché

package com.ht.cache.utils;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;

public class CacheManagerUtils {
    private static final CacheManager manager;
    
    static {
        manager = CacheManager.create(
            CacheManagerUtils.class.getResourceAsStream("/ehcache.xml")
        );
    }
    
    public static void guardar(String nombreCache, Object clave, Object valor) {
        Cache cacheActual = manager.getCache(nombreCache);
        if (cacheActual == null) {
            manager.addCache(nombreCache);
            cacheActual = manager.getCache(nombreCache);
        }
        cacheActual.put(new Element(clave, valor));
    }
    
    public static Object recuperar(String nombreCache, Object clave) {
        Element elemento = manager.getCache(nombreCache).get(clave);
        return elemento != null ? elemento.getObjectValue() : null;
    }
}

Integración con Hibernate

Caché de primer nivel (sesión)

Hibernate implementa un caché por sesión que almacena entidades consultadas. Múltiples consultas dentro de la misma sesión recuperan datos del caché sin generar consultas SQL adicionales.

Configuración del caché de segundo nivel

En el archivo hibernate.cfg.xml:

<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.region.factory_class">
    org.hibernate.cache.ehcache.EhCacheRegionFactory
</property>
<property name="hibernate.cache.use_query_cache">true</property>

En el mapeo de la entidad:

<cache usage="read-write" region="com.entities.User"/>

Uso del caché de consultas

Session sesion = sessionFactory.openSession();
Transaction transaccion = sesion.beginTransaction();

// Consulta con caché activado
Query consulta = sesion.createQuery("FROM User");
consulta.setCacheable(true);
List resultados = consulta.list();

// Segunda consulta - recupera del caché
List resultados2 = consulta.list();

transaccion.commit();
sesion.close();

Para entidades individuales, Hibernate utiliza automáticamente el caché de primer nivel. Para consultas HQL o Criteria, se requiere activar explícitamente el caché mediante setCacheable(true).

Etiquetas: Hibernate ehcache second-level-cache java-orm performance-optimization

Publicado el 7-5 05:00