O Hibernate é um poderoso container que auxilia nas tarefas que envolvem o banco de dados, fazendo com que o desenvolvedor trabalhe diretamente com objetos e deixe que o Hibernate se encarregue de fazer a “tradução” de objetos para tabelas (relacional).
Este artigo aplica-se a um dos milhares de conceitos envolvidos neste poderoso container, a estratégia de busca, principalmente utilizando a linguagem HQL (Hibernate Query Language).
Quando uma pesquisa é feita no Hibernate, ele utiliza umas das estratégias de busca que serão mostradas abaixo, e o uso adequado destas pode trazer uma melhora significativa no desempenho da sua aplicação.
Estratégias de Busca (Fetching Strategies)
São 4 as estratégias utilizadas para busca de dados com o Hibernate:
- Join
- Select
- Subselect
- Batch-size
Estratégia SELECT
Se você tem uma classe Venda e uma classe ItemVenda, onde a relação de Venda para ItemVenda é um @OneToMany, provavelmente você terá uma coleção de ItemVenda dentro de Venda. Utilizando esta estratégia, quando você carregar uma Venda os itens desta venda não serão carregados até que você explicitamente os chame, ou seja, enquanto você não precisa destes itens eles não serão carregados.
Listagem 1: Código principal
//chama o select da venda
Venda venda = (Venda)session.get(Venda.class, 114);
Set sets = venda.getAllItens();
//chama o select do ItemVenda
for ( Iterator iter = sets.iterator();iter.hasNext(); ) {
ItemVenda item = (ItemVenda) iter.next();
System.out.println(item.getNome());
System.out.println(item.getValor());
Veja na saída abaixo que o Hibernate só constrói o SQL do ItemVenda quando você o chama, ou seja, no laço “for”.
Listagem 2: Saída do Fetch SELECT
Hibernate:
select ...from venda
where venda0_.VENDA_ID=?
Hibernate:
select ...from itemvenda
where itemvenda0_.VENDA_ID=?
Estratégia JOIN
Ao contrário da SELECT, a JOIN desabilita o Lazy Loading, isto significa que ao carregar o objeto Venda, automaticamente todos seus itens são carregados, mesmo que você nunca precise destes.
Ainda seguindo o código da Listagem 1, você poderá ver na Listagem 3 o que muda do FETCH SELECT para FETCH JOIN.
Listagem 3: Saída do Fetch JOIN
Hibernate:
select ...
from
venda venda0_
left outer join
itemvenda itemvenda1_
on venda0_.VENDA_ID=itemvenda1_.VENDA_ID
where
venda0_.VENDA_ID=?
Estratégia SUBSELECT
Para esta estratégia utilizaremos um outro código principal para melhor ilustrá-lo. O que ocorre aqui é a criação de uma “sub-query” para captura das collections. Veja o código principal abaixo.
Listagem 4: Código Principal para Subselect
List<Venda> list = session.createQuery("from Venda").list();
for(Venda venda : list){
Set sets = venda.getAllItens();
for ( Iterator iter = sets.iterator();iter.hasNext(); ) {
ItemVenda item = (ItemVenda) iter.next();
System.out.println(item.getNome());
System.out.println(item.getValor());
}
}
Aplicando a estratégia de SUBSELECT a saída será:
Listagem 5: Saída do SUBSELECT
Hibernate:
select ...
from venda venda0_
Hibernate:
select ...
from
itemvenda itemvenda0_
where
itemvenda0_.VENDA_ID in (
select
venda0_.VENDA_ID
from
venda venda0_
)
Com o subselect serão criados 2 SELECTs, sendo o primeiro para retornar apenas as Vendas e o segundo para retornar os Itens desta venda.
Estratégia Batch Size
Vamos explicar essa estratégia com exemplos, mas antes de iniciarmos tenha em mente o seguinte conceito: “A Estratégia de Batch Size não define quantos registros são carregados dentro das collections. Ao invés disso, ela define quantas collections deverão ser carregadas”.
Veja o exemplo de código abaixo que utilizaremos.
Listagem 6: Código para Exemplo Batch Size
List<Venda> list = session.createQuery("from Venda").list();
for(Venda venda : list){
Set sets = venda.getAllItens();
for ( Iterator iter = sets.iterator();iter.hasNext(); ) {
ItemVenda item = (ItemVenda) iter.next();
System.out.println(item.getNome());
System.out.println(item.getValor());
}
}
Sem utilizar o Batch Size a saída do Hibernate para o código acima, será:
Listagem 7: Saída sem Batch Size
Hibernate:
select ...
from venda venda0_
Hibernate:
select ...
from itemvenda itemvenda0_
where itemvenda0_.VENDA_ID=?
Hibernate:
select ...
from itemvenda itemvenda0_
where itemvenda0_.VENDA_ID=?
Dependendo de quantas Vendas estiverem no banco, ele ficará criando SELECTs e mais SELECTs. Para resolver isso usamos o Batch Size, que transformará a saída acima na saída abaixo (utilizando um Batch Size de tamanho 10).
Listagem 8: Saída com Batch Size = 10
Hibernate:
select ...
from venda venda0_
Hibernate:
select ...
from itemvenda itemvenda0_
where
itemvenda0_.VENDA_ID in (
?, ?, ?, ?, ?, ?, ?, ?, ?, ?
)
O que o Hibernate faz é juntar todos aqueles SELECTS em uma clausula “IN”, o que torna a consulta milhões de vezes mais ágil.
Conclusão
É fato que o entendimento de cada uma dessas estratégias pode fazer diferença na hora de colocar sua aplicação em produção, isso porque a não utilização adequada do mesmo pode trazer diversos problemas: lentidão, estouro de pilha (stack overflow) e principalmente a constante reclamação do usuário.
Uma aplicação não é composta apenas de código e retornos, pelo contrário, este é apenas o baseline desta, há necessidades constantes de análise, projetos, testes e muitos outros recursos que completam a mesma. Se você ainda trabalha com aplicações pequenas com uma ínfima quantidade de dados, poderá não sentir diferença na aplicação das estratégias de busca, mas é ideal e quase que obrigatório o seu aprendizado desde o inicio para ao trabalhar com grandes aplicação não ter surpresas.