Skip to main content

🚀 Alpura Back Java Lib Cache v1.2

Librería común para cache de respuestas en microservicios Spring Boot usando Redis.
Se enfoca en simplicidad: la anotación principal @Cacheable funciona automáticamente sin parámetros y ya genera una clave estable basada en el RequestVO (tabla + filtros). Solo configura parámetros si realmente lo necesitas.

📌 Nota sobre dónde colocar las anotaciones: Las anotaciones de la librería (@Cacheable, @CacheEvict, @CacheEvicts) pueden colocarse tanto en la interfaz del service como en la clase ServiceImpl.


🧩 ¿Qué resuelve?

  • ⚡ Acelera respuestas con cache (Redis) transparente
  • 🔑 Genera claves de cache automáticas y estables
  • 🧹 Permite invalidación selectiva o por patrón
  • 🧱 Evita claves con datos sensibles (token, usuario, etc.)
  • 🧾 Logs claros: CACHE ENCONTRADO / CACHE NO ENCONTRADO

📦 Requisitos

  • Java JDK: 21 (Temurin LTS)
  • Spring Boot: 3.3+
  • Redis: 6+
  • Maven/Gradle

⚙️ Instalación

Maven

<dependency>
<groupId>com.alpura.cache</groupId>
<artifactId>alpura-back-java-lib-cache</artifactId>
<version>1.2</version>
</dependency>

Gradle

implementation "com.alpura:alpura-back-java-lib-cache:1.0.0"

💡 La librería trae un aspecto AOP (RedisCacheAspect) que intercepta métodos de tu app para aplicar cache.


🛠️ Configuración mínima

application.yml


redis:
host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD:}
timeout: 2000

✅ Con esto ya puedes anotar métodos y obtener cache. No requiere configuración adicional para que funcione la clave automática.


🔑 Clave automática (sin parámetros)

La clave por defecto para @Cacheable se construye a partir de:

  • RequestVO.parameters.nombreTabla
  • RequestVO.parameters.filtros (ordenado por nombre de campo)

Se excluyen del fingerprint campos sensibles como token, macAddress, userLoggedInfo y otros metadatos.
Esto asegura que dos peticiones con la misma tabla y mismos filtros compartan cache, aunque cambie el token u otros headers.

🧠 Si no usas RequestVO, la librería cae en fallback y usa todos los parámetros del método para construir una huella estable (SHA-256).


🧪 Uso rápido (sin parámetros)

@Cacheable // clave automatica: RequestVO.parameters.{nombreTabla, filtros}
public ResponseVO<Map<String, Object>> listar(RequestVO<Void> request) {
// tu logica de negocio
}

✅ Así de simple. No necesitas pasar key, ttl o cacheNull. La librería decide por ti y loguea los eventos.


🧭 Parámetros opcionales (solo si los necesitas)

@Cacheable(
key = "cat:tabla:#{request.parameters.nombreTabla}:p#{request.size}:o#{request.offset}",
ttl = 300, // segundos
cacheNull = false // no cachear null
)
public ResponseVO<Map<String, Object>> listar(RequestVO<Void> request) { ... }
  • key: plantilla con expresiones #{param.prop} sobre los argumentos del método.
  • ttl: tiempo de vida en segundos. 0 = sin expiración (usa con cuidado).
  • cacheNull: habilita negative caching (guardar explicitamente null).

🧰 Anotaciones disponibles

@Cacheable (lectura)

  • Aplica cache a la salida del método.
  • Clave automática si no defines key.
  • Respeta ttl y cacheNull si los defines.

@CacheEvict (invalidación puntual)

  • Elimina 1 clave exacta o por patrón si usas *.
@CacheEvict(key = "cat:tabla:#{request.parameters.nombreTabla}:*")
public SingleResponseVO<Boolean> invalidarPorTabla(RequestVO<Void> request) {
return SingleResponseVO.ok(true);
}

🧽 Ideal para “limpiar” cache cuando actualizas datos de una tabla concreta.

@CacheEvicts (invalidación múltiple)

  • Ejecuta varios @CacheEvict a la vez.
@CacheEvicts({
@CacheEvict(key = "cat:clientes:#{request.parameters.filtros.plaza}:*"),
@CacheEvict(key = "cat:ordenes:#{request.parameters.filtros.plaza}:*")
})
public SingleResponseVO<Boolean> invalidarPorPlaza(RequestVO<Void> request) {
return SingleResponseVO.ok(true);
}

🧱 Ejemplos comunes

1) CRUD simple

// READ por id (cachea la respuesta)
@Cacheable(key = "cat:producto:#{id}")
public SingleResponseVO<ProductoDTO> obtenerPorId(Long id) { ... }

// CREATE: invalida listas relacionadas
@CacheEvict(key = "cat:producto:list:*")
public SingleResponseVO<Long> crear(ProductoCreateDTO dto) { ... }

// UPDATE: invalida detalle e indices de listas
@CacheEvicts({
@CacheEvict(key = "cat:producto:#{dto.id}"),
@CacheEvict(key = "cat:producto:list:*")
})
public SingleResponseVO<Boolean> actualizar(ProductoUpdateDTO dto) { ... }

// DELETE: invalida detalle e indices
@CacheEvicts({
@CacheEvict(key = "cat:producto:#{id}"),
@CacheEvict(key = "cat:producto:list:*")
})
public SingleResponseVO<Boolean> eliminar(Long id) { ... }

2) Listado paginado por filtros

// clave basada en tabla y filtros del RequestVO
@Cacheable
public ResponseVO<Map<String, Object>> listarPaginado(RequestVO<Void> request) {
// filtros esperados en request.parameters.filtros: { page, size, ... }
return servicio.listar(request);
}

💡 Si quieres que page y size afecten la clave, usa una plantilla explícita:

@Cacheable(key = "cat:tabla:#{request.parameters.nombreTabla}:p#{request.size}:o#{request.offset}:#{request.parameters.filtros}")
public ResponseVO<Map<String, Object>> listarPaginado(RequestVO<Void> request) { ... }

3) Invalidación por tabla (simple y directa)

@CacheEvict(key = "cat:tabla:#{request.parameters.nombreTabla}:*")
public SingleResponseVO<Boolean> invalidarPorTabla(RequestVO<Void> request) {
return SingleResponseVO.ok(true);
}

🧹 “invalidarPorTabla” es una operación de limpieza del cache, útil tras cargas masivas o reprocesos.


🧾 Logs que verás

  • CACHE ENCONTRADO → se sirve desde cache
  • CACHE NO ENCONTRADO - REGISTRANDO ENTRADA → ejecuta el método real y guarda
  • ACTUALIZADO O REGISTRADO → se escribió la clave en Redis
  • EVICT(KEY) / EVICT(PATRON) → se invalidó una o varias claves

🔍 Los logs nunca muestran datos sensibles. Solo tabla y filtros relevantes.


🧱 Buenas prácticas

  • Usa la clave automática por defecto siempre que puedas.
  • Define una key explícita solo cuando necesites incluir paginación u otros campos.
  • Evita TTL muy altos en datos muy cambiantes.
  • Usa @CacheEvict tras operaciones create/update/delete.
  • Para invalidaciones amplias, usa patrón: "prefix:*".

🧯 Solución de problemas

“Cambia el token y no cambia el cache”
✔️ Es normal: el token no forma parte de la clave automática. Si los filtros son iguales, se reutiliza cache.

“Cambia page/size y me da el mismo cache”
👉 Usa key explícita que incluya page y size (ver ejemplo de paginado).

“Quiero invalidar todo lo de una tabla”
👉 @CacheEvict(key = "cat:tabla:#{request.parameters.nombreTabla}:*")

“Quiero invalidar varios dominios a la vez”
👉 Usa @CacheEvicts con varias entradas.