🚀 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.nombreTablaRequestVO.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,ttlocacheNull. 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 explicitamentenull).
🧰 Anotaciones disponibles
@Cacheable (lectura)
- Aplica cache a la salida del método.
- Clave automática si no defines
key. - Respeta
ttlycacheNullsi 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
@CacheEvicta 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 cacheCACHE NO ENCONTRADO - REGISTRANDO ENTRADA→ ejecuta el método real y guardaACTUALIZADO O REGISTRADO→ se escribió la clave en RedisEVICT(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
keyexplícita solo cuando necesites incluir paginación u otros campos. - Evita TTL muy altos en datos muy cambiantes.
- Usa
@CacheEvicttras 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.