Fazendo cache de objetos com Hibernate - Parte II
Este artigo vai demonstrar como utilizar o Second Level Cache do Hibernate, que permite ter um maior controle sobre os objetos que estão em cache.
Este artigo vai demonstrar como utilizar o Second Level Cache do Hibernate, que permite ter um maior controle sobre os objetos que estão em cache.
EHCache
O EHCache é o framework default de cache do Hibernate. Para utilizá-lo basta colocar o ehcache.jar no classpath da aplicação. Para utilizar outro framework, como por exemplo o OSCache, é necessário alterar o arquivo hibernate.cfg.xml e incluir a seguinte linha:
<property name="hibernate.cache.provider_class">net.sf.hibernate.cache.OSCacheProvider </property>
Para este artigo, não é necessário alterar esta propriedade, pois vamos assumir o EHCacheProvider (default).
Ativando o cache para a classe Notícia
Para utilizar o cache, é necessário configurar o hibernate.conf.xml, conforme visualizado abaixo.
<mapping resource="model/Noticia.hbm.xml" />
<class-cache class="model.Noticia" usage="read-write" />
Neste exemplo vamos utilizar o tipo “read-write” o qual permite a leitura e gravação de novas informações. Outro tipo que pode ser utilizado é o “read-only” para as informações que não nunca serão atualizadas.
Secon Level Cache
Para configurar as propriedades do cache para a Notícia, é necessário adicionar o arquivo ehcache.xml no classpath da aplicação. Copie este arquivo da pasta de distribuição do Hibernate e insira as seguintes configurações:
<cache name="model.Noticia"
maxElementsInMemory="100"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="false"
/>
Note que neste caso o nome do cache é o nome completo da classe “model.Noticia”. Caso o nome do cache não seja encontrado, o cache default será utilizado. Este cache também é configurado no arquivo ehcache.xml com a tag “<defaultCache>”.
Abaixo as explicações de cada propriedade que pode ser configurada:
Þ maxElementsInMemory: número máximo de objetos que podem ficar armazenados em memória.
Þ eternal: se configurado para “true”, significa que o cache nunca vai expirar.
Þ timeToIdleSeconds: tempo em segundos que um objeto pode permanecer inutilizado no cache.
Þ timeToLiveSeconds: tempo em segundos que um objeto pode ficar em cache.
Þ overflowToDisk: se configurado para “true” e caso o limite de objetos em memória seja superior ao definido pela propriedade “maxElementsInMemory”, as informações serão armazenadas em disco.
Pronto, isto é tudo. Agora os objetos “Notícia” serão armazenados em cache.
Testando o cache
Para testar o cache, vamos fazer o mesmo exemplo que foi utilizado no artigo anterior. Inicialmente é feita uma consulta no banco de dados com a primeira Session. Logo depois a tabela Notícia é atualizada utilizando JDBC. Então a Session atual é fechada, e uma nova é aberta. Através do Second Level Cache, a segunda Session consegue recuperar os objetos em cache, respeitando as configurações de limite de objetos em memória e tempo de expiração definidos no arquivo ehcache.xml.
//1ª consulta…
//JDBC com update…
//fecha a 1ª session (fecha conexão e elimina o first level cache)
t.commit();
session.close();
//cria uma nova session
session = sessionFactory.openSession();
t = session.beginTransaction();
//2ª consulta…
//As informações retornadas aqui estão em cache
Faça o teste, e você verá que o resultado retornado pela 2ª consulta é o mesmo retornado pela 1ª, uma vez que o cache é utilizado, e não é feita uma consulta no banco de dados.
Cache para a Query
A classe Query é frequentemente utilizada para realizar consultas no banco de dados utilizando HQL, como por exemplo:
Noticia n = (Noticia) session.createQuery("from Noticia where id = 1").setCacheable(true).uniqueResult();
Note que para utilizar o cache da Query, o método setCacheable(true) foi chamado para informar o Hibernate que é necessário fazer cache da consulta.
Porém para o cache funcionar, é necessário adicionar a seguinte linha no arquivo hibernate.cfg.xml:
<property name="hibernate.cache.use_query_cache">true</property>
Por default, o nome do cache (conhecido como região) utilizado é “org.hibernate.cache.StandardQueryCache”. Portanto, do mesmo modo que anteriormente foi configurado o cache para a classe Notícia, é necessário configurar as propriedades do cache para a Query, conforme visualizado abaixo:
<cache name="org.hibernate.cache.StandardQueryCache"
…… outras propriedades aqui
/>
Caso seja necessário customizar o cache a ser utilizado por cada consulta, pode-se utilizar o método “setCacheRegion” da classe Query:
Noticia n = (Noticia) session.createQuery("from Noticia where id = 1").setCacheable(true).setCacheRegion("cacheNoticia").uniqueResult();
Desta forma uma configuração de cache precisa ser criada para o nome “cacheNoticia”, conforme demonstrado abaixo:
<cache name="consultaNoticia"
…… outras propriedades aqui
/>
Log4j
O log4j pode ser utilizado para debugar a execução do cache. Para ativar os logs, insira a seguinte linha no arquivo log4j.properties:
log4j.logger.org.hibernate.cache=debug,stdout
Para demonstrar os logs, execute o código abaixo:
public class TesteCache3 {
public static void main(String[] args) throws Exception {
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = sessionFactory.openSession();
Transaction t = session.beginTransaction();
//Recupera a notícia do banco.
System.out.println("1 - pre session.get");
Noticia n = (Noticia) session.createQuery("from Noticia where id = 1").setCacheable(true).setCacheRegion("consultaNoticia").uniqueResult();
System.out.println("1 - pos session.get");
System.out.println(n.getTitulo());
// Fecha a 1ª session
t.commit();
session.close();
// Atualiza a tabela usando JDBC.
Class.forName("org.hsqldb.jdbcDriver");
Connection conn = DriverManager.getConnection("jdbc:hsqldb:hsql://localhost/hello","sa","");
Statement stmt = conn.createStatement();
stmt.executeUpdate("update noticia set titulo = 'novo titulo'");
session = sessionFactory.openSession();
t = session.beginTransaction();
//Faz a busca na 2ª session e utiliza o cache “consultaNoticia”
System.out.println("2 - pre session.get");
n = (Noticia) session.createQuery("from Noticia where id = 1").setCacheable(true).setCacheRegion("consultaNoticia").uniqueResult();
System.out.println("2 - pos session.get");
System.out.println(n.getTitulo());
t.commit();
session.close();
}
}
A saída do programa pode ser visualizada abaixo:
DEBUG CacheFactory:39 - instantiating cache region: model.Noticia usage strategy: read-write
INFO UpdateTimestampsCache:43 - starting update timestamps cache at region: org.hibernate.cache.UpdateTimestampsCache
WARN EhCacheProvider:103 - Could not find configuration [org.hibernate.cache.UpdateTimestampsCache]; using defaults.
DEBUG EhCacheProvider:106 - started EHCache region: org.hibernate.cache.UpdateTimestampsCache
INFO StandardQueryCache:51 - starting query cache at region: org.hibernate.cache.StandardQueryCache
1 - pre session.get
INFO StandardQueryCache:51 - starting query cache at region: consultaNoticia
WARN EhCacheProvider:103 - Could not find configuration [consultaNoticia]; using defaults.
DEBUG EhCacheProvider:106 - started EHCache region: consultaNoticia
DEBUG StandardQueryCache:93 - checking cached query results in region: consultaNoticia
DEBUG EhCache:104 - key: sql: select noticia0_.ID as ID0_, noticia0_.titulo as titulo0_, noticia0_.texto as texto0_ from Noticia noticia0_ where noticia0_.ID=1; parameters: ; named parameters: {}
DEBUG EhCache:113 - Element for sql: select noticia0_.ID as ID0_, noticia0_.titulo as titulo0_, noticia0_.texto as texto0_ from Noticia noticia0_ where noticia0_.ID=1; parameters: ; named parameters: {} is null
DEBUG StandardQueryCache:98 - query results were not found in cache
Hibernate: select noticia0_.ID as ID0_, noticia0_.titulo as titulo0_, noticia0_.texto as texto0_ from Noticia noticia0_ where noticia0_.ID=1
DEBUG ReadWriteCache:148 - Caching: model.Noticia#1
DEBUG EhCache:104 - key: model.Noticia#1
DEBUG EhCache:113 - Element for model.Noticia#1 is null
DEBUG ReadWriteCache:160 - Cached: model.Noticia#1
DEBUG StandardQueryCache:67 - caching query results in region: consultaNoticia
1 - pos session.get
Titulo antigo
2 - pre session.get
DEBUG StandardQueryCache:93 - checking cached query results in region: consultaNoticia
DEBUG EhCache:104 - key: sql: select noticia0_.ID as ID0_, noticia0_.titulo as titulo0_, noticia0_.texto as texto0_ from Noticia noticia0_ where noticia0_.ID=1; parameters: ; named parameters: {}
DEBUG StandardQueryCache:147 - Checking query spaces for up-to-dateness: [Noticia]
DEBUG EhCache:104 - key: Noticia
DEBUG EhCache:113 - Element for Noticia is null
DEBUG StandardQueryCache:108 - returning cached query results
DEBUG ReadWriteCache:75 - Cache lookup: model.Noticia#1
DEBUG EhCache:104 - key: model.Noticia#1
DEBUG ReadWriteCache:85 - Cache hit: model.Noticia#1
2 - pos session.get
Titulo antigo
Conclusão
Neste artigo foi explicado como configurar o Second Level Cache do Hibernate, e como demonstrado é uma tarefa relativamente simples.
Fazer cache de objetos pode aumentar a performance de sua aplicação, mas cuidado para não abusar muito, porque isto pode aumentar consideravelmente a quantidade de memória que sua aplicação está utilizando.
A configuração ideal para cada aplicação, cabe a você descobrir. Até a próxima.

Space do autor

Estudo comparativo entre banco de dados IBM Informix e Microsoft SQL


2
1
Conheça os planos de créditos DevMedia e visualize esse post agora mesmo!