Artigo no estilo: Curso

Do que se trata o artigo:

Neste artigo veremos como utilizar os recursos do Hibernate, agora na versão 4, para facilitar e dinamizar a persistência de objetos em aplicações Java. Nesta etapa, conhecermos as novidades da API de serviços internos e o suporte a Multitenancy, além do tradicional Open Session in View.

Em que situação o tema útil:

A integração com bancos de dados relacionais é muito presente no contexto de desenvolvimento Java, e compreender as ferramentas e recursos disponíveis para realizar esta tarefa da melhor forma possível é fundamental.

Resumo DevMan:

O desenvolvimento de software integrado com banco de dados é muito comum no mercado Java, e o Hibernate é um framework muito adotado por equipes de desenvolvimento para tratar este cenário. Ao trabalhar com o Hibernate, é importante entender como trabalhar com sessões e transações em sistemas Java e suas abordagens, como o padrão Open Session in View. Além disso, novidades como a redefinição de serviços internos do framework e o suporte nativo a Multitenancy tornam o Hibernate ainda mais atrativo no desenvolvimento de novos sistemas. É sobre estes assuntos que abordaremos neste artigo.

Na indústria de desenvolvimento de software, é de praxe que desenvolvedores criem e utilizem frameworks para auxiliar na implementação de seus códigos. Há anos o mercado de desenvolvimento Java tem utilizado frameworks para facilitar a criação e manutenção de sistemas. Dentre os diversos frameworks adotados pela comunidade Java, o Hibernate com certeza é um dos mais utilizados.

O Hibernate é um framework que pertence à restrita lista dos que continuam sendo utilizados como mainstream mesmo depois de diversos anos de seu surgimento. Poucos atingiram este resultado. Para que um framework consiga este feito, e continue a ser utilizado por muitos anos, é fundamental que este esteja atento a mudanças na indústria e se adeque a estas mudanças por meio do lançamento de novas versões.

O Hibernate é uma ferramenta de ORM (Object-Relational Mapping), isto é, auxilia o trabalho de mapeamento objeto-relacional. Mais precisamente, podemos utilizar o Hibernate para trabalhar com objetos em nosso contexto de programação Java e o framework se encarrega de executar o SQL necessário correspondente às operações que envolvam bancos de dados relacionais.

Tendo como base frameworks como o Hibernate, a partir da quinta versão do Java EE surgiu uma API de persistência para bancos de dados relacionais – o JPA (Java Persistence API). A partir de sua criação, tem sido muito comum a utilização do JPA na camada de persistência, encapsulando um framework como o Hibernate como implementação desta especificação.

Apesar da vantagem de se reduzir o acoplamento ao se utilizar uma API ao invés de uma implementação direta, em frameworks como o Hibernate existem recursos que podem ser úteis e ainda não estão contemplados no JPA. Tendo isto em vista, conhecer sobre como melhor aproveitar este framework pode ser importante em sua utilização.

Esta série de artigos busca abranger alguns assuntos pertinentes à utilização deste framework em sistemas Java. Na primeira parte do artigo diversos assuntos foram tratados, como a persistência básica de entidades, mecanismos de recuperação de objetos, relacionamentos entre objetos e o efeito cascata nas operações de persistência. Além disso, discutimos a respeito de problemas encontrados em aplicações Java que possuem impactos na persistência de objetos. Alguns dos assuntos tratados são o caso do anti-pattern modelo anêmico (AnemicDomainModel) e o encapsulamento da lógica da aplicação em um modelo orientado a objetos.

Nesta segunda parte, iremos tratar de assuntos como o controle de sessões pelo Open Session in View, procurando evitar alguns problemas de carregamento preguiçoso (lazy) em aplicações web. Ademais, iremos discutir e exemplificar duas das grandes novidades da versão 4 do Hibernate, a definição de serviços internos e a configuração do mecanismo de Multitenancy.

Controle de sessões e Open Session in View

Como exemplificado na primeira parte do artigo, podemos utilizar o Hibernate para criar, alterar, remover e consultar entidades em nosso sistema de uma forma orientada a objetos. Para que as manipulações como criação, alteração e remoção de objetos sejam de fato efetuadas em nosso banco de dados, é necessário que estas operações estejam no contexto transacional. Portanto, é preciso que uma transação do Hibernate esteja aberta no momento em que estas manipulações são realizadas. Tendo isto em vista, é importante entendermos como trabalhar com sessões e transações em nosso sistema.

Um dos problemas mais encontrados em sistemas Java relacionado ao controle de sessões no Hibernate é o cenário onde exceções do tipo LazyInitializationException são lançadas. Este erro ocorre quando algum carregamento lazy foi acionado sem que a sessão correspondente com o Hibernate esteja devidamente aberta. Quando algum objeto X é carregado de maneira lazy, o Hibernate tenta utilizar a mesma sessão em que realizou o carregamento do objeto Y – objeto que está exigindo o carregamento de X. Isto é, quando realizamos alguma chamada em um objeto Y que retorne o objeto X vinculado, receberemos um objeto falso de X (um proxy) que será carregado apenas quando necessário pela mesma sessão em que Y foi carregado. Neste cenário, caso a sessão que realizou o carregamento de Y esteja fechada, uma exceção do tipo LazyInitializationException será lançada. Por esta causa, muitas vezes este tipo de erro ocorre em chamadas a métodos simples, como uma chamada a algum getter ou setter.

Esse problema deve ser tratado de acordo com o tipo de aplicação Java que está sendo desenvolvida e possui diferentes soluções, variando de acordo com as tecnologias empregadas nas camadas de aplicação e negócio. No caso de sistemas web, uma solução muito discutida, porém muito aplicada, é a utilização do pattern Open Session in View.

O padrão Open Session in View determina que para cada requisição web uma nova sessão do banco de dados seja iniciada, e esta sessão deve permanecer aberta durante todo o período desta requisição, sendo utilizada por qualquer operação realizada para o processamento da requisição e na renderização de sua resposta.

Podemos aplicar este padrão empregando diferentes tecnologias, o que também pode variar de acordo com os frameworks utilizados para o desenvolvimento web do sistema. Apesar disto, este padrão muitas vezes é implementado com o uso de Filters da Servlet API, pois esta API está sempre disponível em qualquer ambiente Java EE. Uma representação visual do funcionamento do padrão Open Session in View pode ser observada na Figura 1, e um exemplo de implementação deste pattern com Filters pode ser visualizado na Listagem 1.

Os exemplos disponibilizados neste artigo representam uma continuação do contexto iniciado no primeiro artigo desta série. Mais detalhes podem ser encontrados na Edição 104 da Java Magazine.

Funcionamento do padrão Open
Session in View

Figura 1. Funcionamento do padrão Open Session in View.

Listagem 1. Implementação exemplo da classe OpenSessionInViewFilter.


  package br.com.simpleit.myforum.filter;
   
  @WebFilter(urlPatterns="/*")
  public class OpenSessionInViewFilter implements Filter{
   
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
              Session session = HibernateUtil.getSession();
              Transaction transaction = session.getTransaction();
              transaction.begin();
              try{
                     chain.doFilter(request, response);
                     transaction.commit();
              }catch(Throwable e){
                     transaction.rollback();
                     throw e;
              }finally{
                     if(session.isOpen())
                            session.close();
              }
        }
   
        @Override
        public void init(FilterConfig arg0) throws ServletException {             
   
        }
   
        @Override
        public void destroy() {    
   
        }
   
  } 

Para implementarmos o recurso do Open Session in View adequadamente, além de garantir que uma mesma sessão estará aberta e disponível durante todo o processamento de uma requisição web, precisamos garantir que todo o comando de banco de dados a ser executado no contexto desta requisição utilize a mesma sessão do Hibernate.

Portanto, quando precisarmos trabalhar com objetos persistentes (por exemplo, em métodos de DAOs), não podemos mais abrir novas sessões a cada método individual de persistência, e sim reutilizar uma sessão que supostamente já foi aberta no contexto desta requisição. Da mesma forma, também não é boa prática abrirmos e fecharmos uma transação para cada pequena operação de banco de dados, pois muitas vezes estas operações já estarão sendo chamadas em um contexto que se deseje manter a atomicidade de um conjunto de diversas operações em algum processamento mais complexo. Por conseguinte, não precisamos trabalhar com transações em nossos objetos DAO e podemos assumir que o controle transacional já está sendo realizado por algum objeto externo (por exemplo, o próprio Open Session in View ou algum aspecto).

Um recurso bem interessante que o Hibernate nos disponibiliza para trabalharmos com esta questão é a definição de uma sessão atual (current session). Por meio da propriedade current_session_context_class, a ser configurada no arquivo hibernate.cfg.xml, podemos indicar qual regra o Hibernate usa para definir o conceito de sessão atual (current session). Ao configurarmos corretamente esta propriedade podemos obter uma sessão atual pela chamada do método getCurrentSession() do objeto SessionFactory, ao invés do método openSession() que estávamos utilizando em nossos exemplos.

Ao configurarmos a propriedade current_session_context_class com o valor org.hibernate.context.internal.ThreadLocalSessionContext, indicamos ao Hibernate que o contexto da sessão atual será dado por uma variável do tipo ThreadLocal, que por sua vez verifica se existe alguma sessão aberta vinculada à thread corrente e devolve esta sessão caso encontrado, criando uma nova caso contrário.

Utilizando o conceito de current session, podemos alterar nossa classe HibernateUtil, definida na primeira parte do artigo, para utilizar alguma sessão previamente criada na mesma thread (e consequentemente na mesma requisição web). A alteração proposta nesta classe pode ser visualizada na Listagem 2.

Listagem 2. Alteração em HibernateUtil para reaproveitar sessão já criada pela classe OpenSessionInViewFilter.


  package br.com.simpleit.myforum.persistence;
   
  //imports omitidos...
   
  public class HibernateUtil {     
   
        private static SessionFactory sessionFactory;  
        static{
              //criação e configuração da sessionFactory
        }
        public static Session getSession() {
              Session session = sessionFactory.getCurrentSession();
              return session;
        }
  } 

Outra opção para gerenciarmos nossas transações em um contexto Java é adotarmos o JTA (Java Transaction API). Esta API está disponível em servidores de aplicação Java EE e é o mecanismo padrão deste ambiente para o controle de transações, permitindo trabalharmos com transações de maneira declarativa ou programática.

Para que as sessões do Hibernate estejam vinculadas ao contexto JTA, podemos simplesmente alterar a propriedade current_session_context_class para org.hibernate.context.JTASessionContext. Desta forma, as sessões requisitadas por nossa aplicação por meio do método getCurrentSession() irão acessar sessões de acordo com o contexto transacional indicado pela API.

Serviços internos

Uma das novidades mais interessantes do Hibernate 4 é a capacidade de redefinir e personalizar os seus serviços internos. A redefinição destes serviços foi o mecanismo criado para permitir a customização de seu comportamento – algo que era muito difícil de fazer nas versões anteriores do framework.

O mecanismo de configuração de serviços é simples e deve ser realizado durante a construção de nossa SessionFactory. No momento da criação da SessionFactory, preparamos um objeto que possui todas as configurações de serviços do Hibernate a serem utilizados em nossas sessões, o ServiceRegistry.

Por sua vez, para construirmos um ServiceRegistry, devemos utilizar a classe ServiceRegistryBuilder e definir quais alterações de quais serviços queremos realizar. Todos os serviços internos do Hibernate disponibilizados por esta API são representados por interfaces, e devemos prover implementações destas interfaces caso desejarmos substituir algum deles.

Todas as interfaces relacionadas com os serviços do Hibernate herdam a interface org.hibernate.service.Service e cada uma representa um tipo de tarefa realizada internamente no framework. Alguns exemplos de serviços internos indicados na documentação do Hibernate que representam algum comportamento interno do framework são:

• org.hibernate.engine.jdbc.batch.spi.BatchBuilder: Define a estratégia de como o Hibernate administra execuções em lote;

• org.hibernate.service.config.spi.ConfigurationService: Disponibiliza o acesso às configurações do Hibernate para outros serviços, combinando tanto as indicadas explicitamente quanto as implicitamente definidas no ambiente;

• org.hibernate.service.jdbc.connections.spi.ConnectionProvider: Define o mecanismo que o Hibernate utiliza para obter conexões com o banco de dados;

...

Quer ler esse conteúdo completo? Tenha acesso completo