martes, 11 de diciembre de 2007

Query Cache en Hibernate

En un post anterior comenté sobre la utilización del First Level y Second Level Cache en Hibernate, otra opción interesante para mejorar la performance y rendimiento de nuestras aplicaciones es el uso del Query Cache.

En ciertos casos, nos interesa "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 Monedas registradas 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 Second Level Cache.
Entonces para el ejemplo que venimos manejando, hay que realizar los siguientes pasos:

  1. Establecer el property hibernate.cache.use_query_cache=true en el hibernate.cfg.xml
  2. Configurar la entidad Moneda como "cacheable", según lo publicado anteriormente
  3. Establecer la consulta como "cacheable":

List monedas = sess.createQuery("from Moneda")
.setCacheable(true)
.list();

Por lo tanto, la primera vez que se ejecuta la consulta, se retorna la lista de Monedas 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.

Ustedes se preguntarán: ¿Que pasa si agrego una nueva Moneda? ¿El query dejaría de ser válido?
Si, así es, si agregamos una nueva Moneda 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 insertamos una Moneda por afuera del aplicativo, es decir con un insert directo a la tabla, o si tenemos 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.

Como habrán concluido, 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, Estados, Tipos y Monedas.

En mi opinión, éste tipo de configuraciones son las que hacen la diferencia a la hora de evaluar el rendimiento o performance de los aplicativos desarrollados con Hibernate.

Hasta el próximo post!

9 comentarios:

Anónimo dijo...

Ya me quedó mas claro...

en mi caso, se lanzan muchas consultas sobre tablas para ser representadas en tablas jsp. Lo que también me interesa es que el resultado de esa consulta se vaya almacenando en la cache de disco. ¿Lo tendría resuelto utilizando ehcache con hibernate para que cachee a disco?

Saludos,

Federico Varela dijo...

enredado, por defecto este tipo de cache se realiza en memoria ram, no en disco, por lo que lo hace mucho mas rápido. Si quieres hacerlo sobre disco te recomiendo que veas la documentación del ehcache y las propiedades diskStore y overflowToDisk.

Saludos

Anónimo dijo...

Hola.
Hago una consulta que devuelve mas de un millon de registros. Usando EHCACHE quiero que lo guarde en disco duro para que no m agote la memoria en Java, y luego vaya leyendo del disco. ¿Es posible?
Gracias

Federico Varela dijo...

Javi, hola, gracias por visitar el blog.
Con respecto a tu consulta, creo que si bien técnicamente sería posible guardar el cache en disco teniendo los recursos de hardware suficientes, te recomiendo que analices si realmente es necesario hacerlo. Usualmente lo que se hace con consultas grandes es paginar el resultado en la base de datos, la forma de implementarlo depende si usas Criteria o HQL, el la documentación hay ejemplos.
Saludos

ESQUI dijo...

Primero muchas gracias, tu articulo es muy claro y realmente me parece muy útil, sobretodo para los que estamos trabajando con Hibernate, principalmente he escuchado muchas quejas acerca de la velocidad que maneja Hibernate, y entiendo que el mapeo mal controlado es mucho el daño que genera.

Ahora, mi pregunta es la siguiente, si deseo cachear un par de tablas, pero estas no se actualizan desde el mismo aplicativo, sino desde otro totalmente diferente, e incluso usando Inserts a lo SQL, existe alguna forma de programar la actualización de la Cache, por ejemplo, cada hora, o algo así.

Un saludo muy cordial.

Federico Varela dijo...

Esqui, gracias por el comentario.
Respecto a tu consulta, una opción sería configurar el cache para que expire cada cierto tiempo, esto depende de la implementación de cache uses, en el caso de EHCache se hace en el archivo ehcache.xml que debe estar disponible en el classpath del aplicativo.
Te recomiendo que veas la doc en http://ehcache.sourceforge.net/EhcacheUserGuide.html, específicamente el atributo timeToLiveSeconds, creo que te puede servir.
Saludos

pallufa19 dijo...

Hola Federico,

Muchísimas gracias por la información sobre la ehcache.xml y el link a la documentación para configurarlo. Me ha sido de mucha ayuda la parte donde comentabas la utilización de timeToLiveSeconds para que la caché expire para un modelo de Hibernate y así obligarlo a ir a la bbdd para solventar problemas si actualizamos algun campo directamente desde la bbdd.

Un saludo!

Federico Varela dijo...

Hola Pallufa, gracias por el comentario, me alegro que te haya servido.
Saludos

Pyter dijo...

hola federico
tengo una cuestion resulta que cuando consulto, luego creo un reg nuevo, y vuelvo a consultar en ocaciones obtengo los registros con el nuevo que di de alta pero en otras obtengo los mismos que en la consulta anterior. que cache deberia de usar y como? saludos !!

buen post