Os 7 hábitos dos desenvolvedores Hibernate e JPA altamente eficazes

Essa última semana tive a oportunidade de palestrar no RioJUG sobre JPA e Hibernate, onde fui muito bem recebido pelo Guilherme Chapiewski e Magno Cavalcante. Isso ocorreu durante o treinamento de Arquitetura Java que demos para diversos desenvolvedores da Globo.com, e onde tive o prazer de conhecer alguns desenvolvedores e arquitetos, como Vitor Pellegrino, Anselmo Alves, Wesley Silva, Alexandre Gazola, Tiago Motta, entre outros. Também vi o Ettore Luglio e o Daniel Passos.


DSC01736 DSC01709
DSC01691 DSC01750

Infelizmente durante a palestra não tive tempo de mostrar muitos recursos avançados e boas práticas do Hibernate, então vou usar este espaço para tal.

Precisamos conhecer todo pontencial de qualquer ferramenta, framework ou biblioteca que vamos usar em um projeto. Uma ferramenta boa, sem o devido conhecimento, resulta em projetos atrasados, com problemas de performance e desculpas do tipo “O problema é o [Hibernate|Struts|JSP, insira sua tecnologia aqui…], que gera uma quantidade excessiva de [queries|objetos|scriptlets|…] durante [lazy loading|requisições|…]”. Isso vale em especial para ferramentas mais antigas, como JSP e Struts 1. Hoje em dia ambas possuem recursos poderosos que auxiliam em muito o desenvolvimento, mas alguns desenvolvedores acabam não se aprofundando e desconhecem esses detalhes que podem ser vitais no uso de determinadas tecnologias.

Com o Hibernate não é diferente. É muito comum as pessoas culparem o Hibernate pela queda do banco de dados, performance das queries, número de objetos em memória, LazyInitializationException, e outros inúmeros problemas os quais em sua maioria poderiam ter sido evitados com a utilização de alguns recursos, boas práticas e bons hábitos no uso desse framework.

Sem mais demora, os 7 hábitos:

Connection Pool – Usar o pool de conexões embutido com o Hibernate é um erro comum, e a própria documentação diz que você não deve usa-lo em produção! Pode acontecer até connections leak!
A Caelum teve ótimas experiências com o C3P0, e é muito fácil configurá-lo como Provider para o Hibernate.

Second Level Cache – Todos já passamos por situações em que precisamos criar caches para as linhas de banco de dados mais acessadas. Aqui temos diversos problemas: sincronismo, gasto de memória, memory leak, tamanho do cache, política de prioridade da fila (LFU, LRU, FIFO, etc), tempo de expiração e modos de invalidar o cache. Escrever um cache eficiente e seguro é um grande trabalho, imagine ainda dar suporte a um cache distribuído e que possa se aproveitar do disco rígido para não gastar tanta memória? Esse é o papel do second level cache. Você pode usá-lo com diversos providers, sendo o EhCache um dos mais conhecidos.

Query Cache – Um recurso fantástico do Hibernate. No caso de você ter queries que são executadas inúmeras vezes, você pode pedir para o Hibernate fazer o cache do resultado desta query. O interessante é que ele não vai armazenar todos os objetos resultantes, e sim apenas suas primary keys: no momento que ele precisar executar novamente aquela query, ele já tem todos os IDs resultantes, e através destes ele consulta o second level cache, sem fazer um único hit ao banco de dados! Esse cache será invalidado quando alguma das tabelas envolvidas nesta query for atualizada, ou um determinado tempo passar.

Controle do Lazy – Algumas pessoas costumam reclamar do lazy loading, dizendo que em alguns casos teria sido melhor ele carregar tudo em uma única query. Você sempre pode redefinir o comportamento desses relacionamentos quando fizer uma query, através de um eager fetch.

Stateless Session – Algumas vezes precisamos fazer um processamento em batch de objetos, ou mesmo inserir uma quantidade grande deles na base de dados. Em muitos casos uma bulk operation é o suficiente, mas se quisermos manter a Orientação a Objetos, devemos tomar cuidado com a grande quantidade de objetos que ficarão armazenados no first level cache. A StatelessSession resolve esse problema: simplesmente não há first level cache e nenhum objeto se comportará como managed, tendo praticamente o mesmo efeito que chamar entityManager.clear() a cada operação.

Open Session in View – Na arquitetura MVC, muitas vezes renderizamos em nossa view diversas entidades do nosso modelo, e essas podem ter sido carregas pelo Hibernate. Se essas entidades possuem relacionamentos lazy, precisamos que a sessão esteja aberta no momento da renderização da View, caso contrário teremos uma LazyInitializaionException ou algum código macarrônico para carregar relacionamentos que nem sempre precisamos. Para isso devemos manter a session aberta através de um filtro, interceptador ou algum outro mecanismo. Isso resulta no pattern Open Session in View e também se aplica ao EntityManager.
O mesmo efeito pode ser obtido através de inversão de controle e injeção de dependências através da anotação @PersistenceContext, que é tratada por containers EJB3 e também por muitos frameworks web, como o Spring. O EJB3 ainda possui o conceito de um contexto de persistência extendido, quem é interessante em casos de conversações longas: o EntityManager usado será o mesmo enquanto aquele stateful session bean não for removido.

Evitando número de queries excessivas (n+1) – Se uma NotaFiscal possui muitos Items, e essa coleção é lazy, gastaremos duas queries para buscar a NotaFiscal e seus respectivos Itens. Mas se temos uma lista de NotaFiscal resultante de uma query, para cada NotaFiscal teremos uma nova query executada para todo getItems invocados. 1 query para listar NotaFiscal, N queries para pegar os relacionamentos: é o problema das n+1 queries. Você deve usar as configurações de batch-size e fetch-size para pedir ao Hibernate carregar as entidades/relacionamentos em blocos em vez de um em um. Você também pode utilizar o second level cache nesses relacionamentos, diminuindo consideravelmente o número de queries disparada.

Essas são apenas alguns dos hábitos, poderíamos ainda falar sobre o bom tratamento de exceções, o cuidado ao fechar todos os recursos abertos pelo Hibernate, o uso de queries nativas, o mapeamento de queries nativas para entidades através do ResultTransformer, filtros de coleções, dynamic insert e update, a criação do seu próprio tipo de persistência, e muitos outros. Conhecer bem o capítulo de performance do Hibernate é fundamental além de um bom começo.

52 Comentários

  1. Adriano 28/10/2016 at 12:28 #

    A maioria das dicas é interessante, mas Open Session in View é o anti-pattern mais absurdo que existe, e nunca deveria ser usado em um sistema sério. É jogar fora uma arquitetura de camadas e optar por um código macarrônico, cheio de problemas transacionais, e ainda descontrole das dependências entre objetos.

  2. Paulo Silveira 31/10/2016 at 23:07 #

    oi Adriano. Muita coisa mudou nesses último ~10 anos desde que o artigo foi escrito. Melhor procurar outras fontes também se o seu caso for específico do hibernate/java/opensession

Deixe uma resposta