Cache do Hibernate – Cache de Primeiro Nível

Bem-vindo ao Tutorial do Exemplo de Cache de Primeiro Nível do Hibernate. Recentemente, examinamos a arquitetura do Hibernate, o mapeamento do Hibernate e como usar HQL para disparar consultas SQL de forma orientada a objetos. Hoje, vamos examinar um dos aspectos importantes do Hibernate – Cache do Hibernate.

Cache do Hibernate

O Cache do Hibernate pode ser muito útil para obter um desempenho rápido da aplicação se usado corretamente. A ideia por trás do cache é reduzir o número de consultas ao banco de dados, reduzindo assim o tempo de processamento da aplicação. O Hibernate vem com diferentes tipos de Cache:

  1. Primeiro Nível de Cache: O cache de primeiro nível do Hibernate está associado ao objeto Session. O cache de primeiro nível do Hibernate é ativado por padrão e não há maneira de desativá-lo. No entanto, o Hibernate fornece métodos por meio dos quais podemos excluir objetos selecionados do cache ou limpar completamente o cache. Qualquer objeto armazenado em uma sessão não será visível para outras sessões e, quando a sessão for fechada, todos os objetos armazenados em cache também serão perdidos.
  2. Cache de Segundo Nível: O cache de segundo nível do Hibernate é desativado por padrão, mas podemos ativá-lo por meio de configuração. Atualmente, EHCache e Infinispan fornecem implementações para o cache de segundo nível do Hibernate, e podemos utilizá-los. Vamos explorar isso no próximo tutorial sobre o cache do Hibernate.
  3. Cache de Consulta: O Hibernate também pode armazenar em cache o conjunto de resultados de uma consulta. O Cache de Consulta do Hibernate não armazena o estado real das entidades na memória cache; ele armazena apenas valores de identificação e resultados de tipos de valor. Portanto, deve sempre ser usado em conjunto com o cache de segundo nível.

Exemplo de Cache do Hibernate – Primeiro Nível de Cache

Para o meu programa de exemplo de cache de primeiro nível do Hibernate, estou usando a mesma configuração que em Exemplo HQL, você pode verificar isso e configurar as tabelas e populá-las com dados fictícios. Vamos primeiro analisar o programa, sua saída e depois passaremos por alguns dos pontos importantes relacionados ao Cache de Primeiro Nível do Hibernate. HibernateCacheExample.java

package com.journaldev.hibernate.main;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

import com.journaldev.hibernate.model.Employee;
import com.journaldev.hibernate.util.HibernateUtil;

public class HibernateCacheExample {

	public static void main(String[] args) throws InterruptedException {
		
		SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
		Session session = sessionFactory.getCurrentSession();
		Transaction tx = session.beginTransaction();
		
		//Obter funcionário com id=1
		Employee emp = (Employee) session.load(Employee.class, new Long(1));
		printData(emp,1);
		
		//Aguardando um tempo para alterar os dados no backend
		Thread.sleep(10000);
		
		//Buscar os mesmos dados novamente, verificar logs que nenhuma consulta foi disparada
		Employee emp1 = (Employee) session.load(Employee.class, new Long(1));
		printData(emp1,2);
		
		//Criar nova sessão
		Session newSession = sessionFactory.openSession();
		//Obter funcionário com id=1, notice the logs for query
		Employee emp2 = (Employee) newSession.load(Employee.class, new Long(1));
		printData(emp2,3);
		
		//INÍCIO: exemplo de evict para remover objeto específico do cache de primeiro nível do hibernate
		//Obter funcionário com id=2, primeira vez, portanto consulta nos logs
		Employee emp3 = (Employee) session.load(Employee.class, new Long(2));
		printData(emp3,4);
		
		//evict do objeto funcionário com id=1
		session.evict(emp);
		System.out.println("Session Contains Employee with id=1?"+session.contains(emp));

		//como o objeto foi removido do cache de primeiro nível, você verá a consulta nos logs
		Employee emp4 = (Employee) session.load(Employee.class, new Long(1));
		printData(emp4,5);
		
		//este objeto ainda está presente, então você não verá consulta nos logs
		Employee emp5 = (Employee) session.load(Employee.class, new Long(2));
		printData(emp5,6);
		//FIM: exemplo de evict
		
		//INÍCIO: exemplo de clear para remover tudo do cache de primeiro nível
		session.clear();
		Employee emp6 = (Employee) session.load(Employee.class, new Long(1));
		printData(emp6,7);
		Employee emp7 = (Employee) session.load(Employee.class, new Long(2));
		printData(emp7,8);
		
		System.out.println("Session Contains Employee with id=2?"+session.contains(emp7));
		
		tx.commit();
		sessionFactory.close();
	}

	private static void printData(Employee emp, int count) {
		System.out.println(count+":: Name="+emp.getName()+", Zipcode="+emp.getAddress().getZipcode());
	}

}

Ao executarmos o exemplo acima, a saída contém muitas informações relacionadas ao Hibernate. Mas estamos principalmente interessados na saída específica do nosso código e nas consultas disparadas pelo Hibernate para carregar os dados. O trecho de saída se parece com o seguinte.

Hibernate Configuration loaded
Hibernate serviceRegistry created
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
1:: Name=Pankaj, Zipcode=95129
2:: Name=Pankaj, Zipcode=95129
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
3:: Name=PankajK, Zipcode=95129
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
4:: Name=David, Zipcode=95051
Session Contains Employee with id=1?false
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
5:: Name=Pankaj, Zipcode=95129
6:: Name=David, Zipcode=95051
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
7:: Name=Pankaj, Zipcode=95129
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
8:: Name=David, Zipcode=95051
Session Contains Employee with id=2?true

Pontos Importantes sobre o Cache de Primeiro Nível no Hibernate

Pontos importantes sobre o cache de primeiro nível no Hibernate que podem ser derivados do programa acima são:

  1. O cache de primeiro nível do Hibernate está habilitado por padrão, não são necessárias configurações para isso.
  2. O cache de primeiro nível do Hibernate é específico da sessão, por isso, quando obtemos os mesmos dados na mesma sessão, não há consulta realizada, enquanto em outra sessão a consulta é feita para carregar os dados.
  3. O cache de primeiro nível do Hibernate pode conter valores antigos, como você pode ver acima, coloquei meu programa para dormir por 10 segundos e nesse tempo atualizei o valor (nome de Pankaj para PankajK) no banco de dados, mas não foi refletido na mesma sessão. Mas em outra sessão, obtivemos o valor atualizado.
  4. Podemos usar o método evict() da sessão para remover um único objeto do cache de primeiro nível do Hibernate.
  5. Podemos usar o método clear() da sessão para limpar o cache, ou seja, excluir todos os objetos do cache.
  6. Podemos usar o método contains() da sessão para verificar se um objeto está presente no cache do Hibernate ou não, se o objeto for encontrado no cache, ele retorna verdadeiro, caso contrário, retorna falso.
  7. Como o Hibernate armazena todos os objetos no cache de primeiro nível da sessão, ao executar consultas em massa ou atualizações em lote, é necessário limpar o cache em intervalos específicos para evitar problemas de memória.

Isso é tudo para o exemplo de cache do Hibernate e cache de primeiro nível, em posts futuros, vamos examinar a implementação do Cache de Segundo Nível do Hibernate – EHCache.

Source:
https://www.digitalocean.com/community/tutorials/hibernate-caching-first-level-cache