O documento discute como usar o Hibernate de forma otimizada para evitar problemas de performance. A promessa do Hibernate de trabalhar apenas com objetos não é cumprida na prática para entidades complexas. É necessário mapear relacionamentos como LAZY, fazer projeções para recuperar apenas dados necessários e, em alguns casos, abandonar o Hibernate e otimizar consultas SQL diretamente.
2. Hibernate Otimizado
S Hibernate
S O Hibernate é um framework para o mapeamento objeto-
relacional escrito na linguagem Java. Este framework facilita
o mapeamento dos atributos entre uma base tradicional de
dados relacionais e o modelo objeto de uma aplicação,
mediante o uso de arquivos XML ou anotações Java.
3. Hibernate Otimizado
S A principal promessa do Hibernate
S Quando a gente começa a estudar o Hibernate, a principal
promessa para vender o produto é: O Hibernate gera as
chamadas SQL e libera o desenvolvedor de trabalhar com os
dados de forma relacional, desde que os mapeamentos das
classes de domínio tenham sido realizados, o desenvolvedor
irá trabalhar apenas com objetos
S Então oba
S Eu tenho o id da entidade e para recuperar seus dados eu vou no
meu Dao que usa Hibernate e faço:
Entidade e = dao.findById(1);
S E tenho o objeto “e” populado, simples!
4. Hibernate Otimizado
S O problema é que essa promessa não é cumprida
S Na prática, apenas se for uma entidade de domínio com
poucos dados e sem relacionamento com outras entidades do
sistema isso vai funcionar
public class TipoAluno{
@Id
private int id;
@Column(name = "descricao")
private String descricao;
}
5. Hibernate Otimizado
S Normalmente no seu sistema você terá apenas 10 ou 20 tipos de
alunos e serão sempre recuperados apenas o id e a descrição
S Para entidades que possuem dezenas de milhares de linhas
na tabela para cima, e que possuam muitos
relacionamentos essa abordagem deixar o seu sistema
lendo e não escalável.
7. Hibernate Otimizado
S O que vai ocorrer se você for usar “orientação a objetos” e
fizer um findById na entidade Aluno?
S O Hibernate vai recuperar todas as informações do Aluno,
+todas as informações dos tipo de alunos + todas as
informações das disciplinas associadas ao aluno + todas as
informações das entidades associadas a disciplinas e assim
sucessivamente, até o nível máximo definido nas
configurações do Hibernate
S Multiplique por 1000 usuários fazendo essa consulta. Já
viu o problema ne?
8. Hibernate Otimizado
S Já sei! Troco o mapeamento de EAGER para LAZY.
S Resolve em parte, o Hibernate não irá trazer os dados dos
relacionamentos, porém a cada vez que você fizer
aluno.getDisciplinas() com a sessão aberta ele gerará N
consultas extras para recuperar as disciplinas. Onde N é o
número de disciplinas no aluno.
S Se no sistema existem 1000 alunos consultando suas
disciplinas e, em média, cada aluno está matriculado em 5
disciplinas, em vez de 1.000 consultas banco, o Hibernate
gerará 1.000 iniciais + 5.000 consultas para recuperar
informações da disciplinas
9. Hibernate Otimizado
S Se a sessão do Hiberante já estiver fechada, você passará
por um problema bastante conhecido de
LazyInitializationException. E um erro será gerado para
o usuário
10. Hibernate Otimizado
S Então como eu resolvo isso?
S Simples! Mapeie tudo o LAZY, faça projeção das suas
consultas, recupere apenas os dados necessários e popule o
seu objeto.
SELECT aluno.id, aluno.nome, tipo.descricao
FROM Aluno aluno
INNER JOIN aluno.tipo tipo
WHERE aluno.id = :idAluno
11. Hibernate Otimizado
S Essa consulta irá retornar uma lista de arrays de objetos
List<Object[]> dados= q.list();
S Existem alguns métodos utilitário que podem ser usado
para popular os objetos a partir a lista retorna pela consulta
12. Hibernate Otimizado
S Isso resolverá 90% dos problemas
de performance do seu sistema
S Mas é muito trabalho? Eu sei! mas você
quer qualidade no seu sistema ou não?
13. Hibernate Otimizado
S Quando eu falo em adicionar projeção são significa fazer
isso:
SELECT aluno.*
FROM Aluno aluno
WHERE aluno.id = :idAluno
S Isso não é fazer projeção!
14. Hibernate Otimizado
S E os outros 10% ?
S Esses 10% entram os caos de uso que são muitos usado ou
casos de uso onde o requisito de performance é fundamental
S Nestes casos você deve utilizar diretamente SQL e tem que
“escovar os bits” da sua consulta SQL para ver como ela
pode ser otimizada
S O Hibernate nesses casos dá nem pro gasto.
15. Hibernate Otimizado
S Como eu sei o que otimizar?
S Por exemplo, no PostgreSQL existe uma opção chamada
“Explain Query" que mostra como o banco está
realizando a sua consulta
S No exemplo a seguir a consulta SQL deveria retorna os 20
primeiros resultados de um JOIN entre duas tabelas
17. Hibernate Otimizado
S Apesar da quantidade de dados retornado ser pequena, o
limite só pode ser aplicado depois que as condições do
JOIN foram testadas, então o Banco de dados irá realizar o
Seq Scan em todas as tabelas para realizar o JOIN.
S Seq Scan em tabelas grandes sempre é um problema
S Isso quer dizer que ele precisa percorrer varias tabelas
com milhares de resultados para realizar a consulta. O
que demora em média 500ms.
S Super rápido, mas pense que 100 usuários podem estar
utilizando essa consulta, então são 50 segundos em
produção de uso do banco de dados.
19. Hibernate Otimizado
S Como eu só quero um conjunto pequeno de dados, a saída
foi realizar uma sub consulta menor antes, que restringisse
o meu conjunto de resultados antes de realizar o JOINs da
consulta principal
20. Hibernate Otimizado
SELECT s.id_servidor, p.id_pessoa
FROM rh.servidor
JOIN JOIN comum.pessoa p ON....
WHERE s.id_servidor IN (
-------------------------------------------------
--- Otimização, só recupera desse conjunto pequeno de dados ---
SELECT s.id_servidor FROM ... WHERE ...
LIMIT 100
-------------------------------------------------
)
LIMIT 20
21. Hibernate Otimizado
S Isso fez o tempo da consulta cair de 500ms para 17ms.
Que para 100 usuários o tempo total de uso do banco cai
de 50 segundos para 1,7segudos.
22. Hibernate Otimizado
S Outro exemplo de otimização de consultas SQLs
S Tenho um problema onde se deveria verificar se um
usuário estava entre os resultados retornados por uma
determina consulta.
S Como isso foi realizado a princípio?
24. Hibernate Otimizado
S Porém essa consulta interna pode retornar um número
muito grande dados, na ordem de 2.000.000 de ids para
verificar se o usuário que você quer, está dentro desse
conjunto
S Resultado: 1 segundo a consulta x 100 usuários : 100
segundos de uso do banco.
S Será que não existe uma maneira mais otimizada de fazer
isso?
25. Hibernate Otimizado
S Se você parar para pensar um pouco, verá que se eu quero
verificar se um determinado usuário está no grupo da
consulta é só verificar se:
( count(*) consulta AND meu usuário ) > 0
27. Hibernate Otimizado
S Resultado: De 1 segundo o tempo da consulta caiu para
17ms , para 100 usuários : 1,7 segundos de uso do banco.
+- 5.800 % de ganho do tempo da consulta. Só isso. É pouco?
28. Hibernate Otimizado
S Conclusões
S O Hibernate se tornou o framework padrão para
mapeamento objeto relacional, porém o seu mau uso trás
sérios problemas de performance ao sistema.
S O mapeamento dos relacionamentos do domínio como
LAZY e a recuperação apenas das informações necessárias
ao seu caso de uso resolve 90% das questões de performance
do seu sistema
S Os demais 10%, onde o requisito de performance é
fundamental, deve abandonar o Hibernate e analisar os
pontos mais demorados da sua consulta e tentar descobrir
maneiras de otimizá-la
S Esses 10% vai muito da experiência e habilidade do
desenvolvedor
29. Hibernate Otimizado
S Conclusões
S Falei apenas das otimizações em relação ao tempo das
consultas, existe ainda outras maneiras de otimizar consultas
como criar cache em memória ou desnormalização das
tabelas do banco de dados. Ou ainda, em casos específicos, o
uso de banco de dados não relacionais.