Implementar Cache de Segundo Nível de Hibernate com NCache

Neste tutorial, exploraremos a implementação de um cache de segundo nível em Hibernate usando NCache.

Vamos configurar um aplicativo Java com Hibernate. Em seguida, configuraremos NCache como cache de segundo nível. Finalmente, testaremos a implementação para ver como o cache reduz a carga no banco de dados e melhora o desempenho.

Visão Geral

Antes de mergulharmos na implementação, vamos entender os fundamentos de Hibernate, NCache e o cache de segundo nível de Hibernate.

Hibernate

Hibernate é um framework de mapeamento objeto-relacional (ORM) de código aberto para aplicações Java. Ele simplifica a construção de interações com bancos de dados, através de mapeamento de objetos Java para tabelas de banco de dados e vice-versa.

Para melhorar o desempenho, Hibernate fornece dois níveis de cache:

1. Cache de Primeiro Nível

O cache de primeiro nível está associado com a sessão Hibernate e está habilitado por padrão. Ele armazena os objetos recuperados durante uma sessão, eliminando a necessidade de acessar o banco de dados várias vezes para o mesmo objeto.

O cache de primeiro nível é limitado ao escopo de uma sessão e não é compartilhado entre sessões.

Também não é persistente e é limpo quando a sessão é fechada ou limpa explicitamente.

2. Cache de Segundo Nível

O cache de segundo nível é compartilhado entre sessões e pode ser configurado para armazenar dados no nível de aplicação. Ele reduz o número de acessos à base de dados armazenando objetos por um período maior.

O cache de segundo nível precisa ser configurado explicitamente e pode ser implementado usando vários fornecedores de cache, como NCache, Ehcache, etc.

NCache

NCache é uma solução de cache distribuído para.NET e aplicações Java. Ele fornece uma loja de dados em memória que pode ser usada para cachear dados acessados frequentemente e melhorar o desempenho da aplicação.

NCache suporta várias topologias de cache, como replicadas, particionadas e cliente em cache.

NCache pode ser usado como cache de segundo nível em Hibernate para armazenar e recuperar objetos do cache em vez de acessar a base de dados.

Configuração de Código

Vamos começar criando uma aplicação Java e configurar Hibernate para interagir com a base de dados.

Usaremos Maven para gerenciar dependências e construir o projeto. A aplicação terá uma classe de entidade para definir os dados e uma classe de cliente para interagir com a base de dados.

Primeiro, vamos testar a aplicação sem cache para ver as interações com a base de dados. Em seguida, vamos configurar NCache como cache de segundo nível em Hibernate para cachear objetos de entidade e reduzir os acessos à base de dados.

Dependências

Vamos começar adicionando as dependências necessárias para Hibernate e NCache no arquivo pom.xml:

<dependencies> <!-- Hibernate dependencies --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>6.6.1.Final</version> </dependency> <!-- NCache dependencies --> <dependency> <groupId>com.alachisoft.ncache</groupId> <artifactId>ncache-hibernate</artifactId> <version>5.3.3</version> </dependency> </dependencies>

Atenção: As versões mencionadas aqui podem variar com as últimas versões. Certifique-se de usar as versões apropriadas para o seu projeto.

Classe de Entidade

Agora, vamos criar uma classe de entidade para representar os dados que queremos cachear. Vamos definir uma simples classe Cliente com campos id e nome:

@Entity @Cacheable @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE, region = "CustomerRegion") public class Customer { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; // getters and setters }

A classe Cliente está anotada com @Entity e @Cacheable para defini-la como uma entidade cacheável. Tem um campo id anotado com @Id e @GeneratedValue para gerar identificadores únicos automaticamente.

Nós também usamos a anotação @Cache para especificar a estratégia de cache e região para a entidade.

NONSTRICT_READ_WRITE diz ao Hibernate para atualizar o cache quando dados forem lidos ou escritos com consistência eventual.

O atributo region especifica a região de cache onde os dados serão armazenados.

Vamos configurar o NCache para usar essa região para armazenar os dados em cache.

Configuração do Hibernate

Nós precisamos configurar o Hibernate para interagir com o banco de dados e habilitar o cache.

Vamos criar um arquivo hibernate.cfg.xml no diretório src/main/resources com a seguinte configuração:

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property> <property name="hibernate.connection.url">jdbc:oracle:thin:@localhost:1521:xe</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">password</property> <property name="hibernate.dialect">org.hibernate.dialect.Oracle12cDialect</property> <property name="hibernate.hbm2ddl.auto">update</property> <property name="hibernate.show_sql">true</property> </session-factory> </hibernate-configuration>

Neste arquivo de configuração, especificamos os detalhes de conexão do banco de dados, dialeto e configurações de cache. Usamos o Oracle como o banco de dados e configuramos o Hibernate para atualizar o esquema automaticamente.

Classe de Cliente

Vamos criar uma classe de cliente para interagir com o banco de dados e testar o mecanismo de cache. Podemos escrever uma classe main para salvar e recuperar objetos Customer usando Hibernate:

public class HibernateClient { public static void main(String[] args) { SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory(); Session session = sessionFactory.openSession(); Transaction transaction = session.beginTransaction(); Customer customer = new Customer(); customer.setName("John Doe"); session.save(customer); transaction.commit(); session.close(); session = sessionFactory.openSession(); transaction = session.beginTransaction(); Customer retrievedCustomer = session.get(Customer.class, customer.getId()); System.out.println("Retrieved Customer: " + retrievedCustomer.getName()); transaction.commit(); session.close(); session = sessionFactory.openSession(); transaction = session.beginTransaction(); retrievedCustomer = session.get(Customer.class, customer.getId()); System.out.println("Retrieved Customer: " + retrievedCustomer.getName()); transaction.commit(); session.close(); } }

Na classe de cliente, criamos uma SessionFactory usando a configuração do Hibernate e abrimos uma sessão para interagir com o banco de dados. Salvemos um objeto Customer no banco de dados e o recuperamos usando o id.

Então, chamamos o método retrieveCustomer duas vezes em duas sessões separadas para ver as interações do banco de dados sem cache.

Vamos observar que o banco de dados é acessado duas vezes para recuperar o mesmo objeto:

Como podem ver, a consulta do Hibernate é executada duas vezes.

Configurando o Servidor NCache

Para usar o NCache como cache de nível dois no Hibernate, precisamos configurar um servidor NCache e configurá-lo para armazenar os dados em cache.

O NCache fornece uma solução de cache distribuído que pode ser instalada em servidores Windows e Linux. Vamos criação de um cluster de cache usando o NCache e o configurar. Uma vez que o cluster de cache estiver configurado, podemos se conectar a ele a partir de nossa aplicação Java.

Habilitando o NCache como provedor de Cache de Nível Dois no Hibernate

Uma vez que o servidor NCache estiver configurado, podemos configurar o Hibernate para usar o NCache como provedor de cache de nível dois. Atualizaremos o arquivo de configuração do Hibernate e especificaremos as configurações de cache para habilitar o cache.

Habilitando as Regiões de Cache

Vamos atualizar as configurações de cache no arquivo hibernate.cfg.xml para habilitar o NCache como provedor de cache de nível dois:

<hibernate-configuration> <session-factory> <property name="hibernate.cache.use_second_level_cache">true</property> <property name="hibernate.cache.region.factory_class">com.alachisoft.ncache.NCacheRegionFactory</property> ... </session-factory> </hibernate-configuration>

Aqui, definimos a propriedade hibernate.cache.use_second_level_cache como true para habilitar o cache de nível dois. Também especificamos a propriedade hibernate.cache.region.factory_class para usar a NCacheRegionFactory, uma implementação da JCacheRegionFactory, como provedor de cache.

Configurando Propriedades do NCache

O NCache fornece um conjunto de propriedades para configurar as definições de cache no Hibernate. Podemos especificá-las no arquivo ncache-hibernate.xml:

<configuration> <application-config application-id="myapp" enable-cache-exception="true" default-region-name="DefaultRegion" key-case-sensitivity="false"> <cache-regions> <region name="CustomerRegion" cache-name="demoCache" priority="AboveNormal" expiration-type="Sliding" expiration-period="8"/> <region name="DefaultRegion" cache-name="demoCache" priority="default" expiration-type="None" expiration-period="0"/> </cache-regions> <database-dependencies> <dependency entity-name="hibernator.BLL.Customer" type="oledb" sql-statement="SELECT CustomerID FROM Customers WHERE CustomerID ='?';" cache-key-format="Customers#[pk]" connection-string="Provider=SQLOLEDB;Data Source=20.200.20.40,1433;Initial Catalog=Northwind;User ID=john;Password=1234;"/> </database-dependencies> </application-config> </configuration>

Neste arquivo de configuração, definimos a região de cache CustomerRegion com o nome do cache, a prioridade, o tipo de expiração e o período de expiração. Configuramos o expiration-type para Sliding com um período de expiração de 8 segundos. Isso significa que os dados em cache expirarão após 8 segundos de inatividade e serão removidos do cache.

Adicionalmente, definimos uma DefaultRegion com configurações padrão para outras entidades que não têm uma região de cache específica. Isso serve como uma região de fallback para entidades que não estão explicitamente configuradas.

Haver multiplas regiões de cache permite que definamos diferentes configurações de cache para diferentes entidades com base em suas exigências.

A seguir, definimos uma dependência de banco de dados para a entidade Customers. Isso é usado para manter o cache sincronizado com o banco de dados e atualizar/remover dados em cache quando houver alterações no banco de dados.

Especificamos a instrução SQL para recuperar o CustomerID da tabela Customers e a string de conexão para se conectar ao banco de dados.

O formato da chave de cache especifica como a chave de cache é gerada com base na chave primária da entidade.

Testes

Agora que configuramos o NCache como o cache de nível dois em Hibernate, vamos testar a aplicação para ver como o cache melhora o desempenho. Vamos executar a classe de cliente novamente e observar as interações com o banco de dados com o cache habilitado.

Ao executarmos a classe de cliente, veremos que a primeira chamada para recuperar o objeto Customer atinge o banco de dados para carregar os dados. No entanto, a segunda chamada para recuperar o mesmo objeto o fará carregá-lo do cache em vez de atingir o banco de dados novamente. Isso demonstra como o cache reduce a carga do banco de dados e melhora o desempenho servindo dados do cache.

Benefícios de Usar NCache Com Hibernate

Usar o NCache como cache de nível dois em Hibernate oferece vários benefícios:

  • Performance melhorada: O NCache fornece um armazenamento em memória rápido para dados em cache, reduzindo a latência e melhorando a throughput. Ele também fornece operações assíncronas para atualizar o cache em segundo plano, reduzindo o impacto no desempenho do aplicativo.
  • Escalabilidade: Ao aumentar a escala do aplicativo, o NCache pode escalar horizontalmente para lidar com grandes quantidades de dados e pedidos de usuários. Ele pode ser implantado em um cluster e suporta recursos como replicação de cache e particionamento para distribuir a carga.
  • Flexibilidade: O NCache fornece varias topologias de cache e configurações para atender a diferentes necessidades de aplicativos. Além disso, devido ao uso de regiões de cache, diferentes entidades podem ter configurações de cache diferentes baseadas nas suas necessidades. Isto permite um controle granular sobre o comportamento de cache.
  • Sincronização: O NCache oferece a opção de sincronização com o banco de dados para manter o cache sincronizado com o banco de dados. Isso garante que os dados em cache estejam atualizados e refletem as últimas mudanças feitas no banco de dados.

Resumo

Neste tutorial, nós exploramos a implementação the um cache de nível dois em Hibernate usando o NCache para aplicações Java.

Começamos entendendo os fundamentos de Hibernate, NCache, e o cache de nível dois de Hibernate. Em seguida, nós configuramos um aplicativo Java com Hibernate e configuramos o NCache como cache de nível dois. Finalmente, nós testamos a implementação para ver como o cache melhora o desempenho, reduzindo chamadas ao banco de dados e servindo dados do cache.

Ao usar o NCache com Hibernate, os desenvolvedores podem melhorar o desempenho, a escalabilidade e a confiabilidade das suas aplicações, aproveitando o poder de cache distribuído. O NCache fornece uma solução de cache robusta que integra-se sem problemas com Hibernate e outros frameworks Java, tornando-se uma opção ideal para cache de dados em aplicações Java.

Source:
https://dzone.com/articles/implement-hibernate-second-level-cache-with-ncache