Descrição do problema

Como dissemos acima, a anotação LazyCollectionOption.EXTRA tem um problema - ela executa uma solicitação separada para o banco de dados para cada objeto. Precisamos explicar de alguma forma ao Hibernate que queremos que ele carregue imediatamente todos os objetos filhos para nossos objetos pais.

Os desenvolvedores do Hibernate criaram uma solução para este problema, o operador join fetch em HQL.

Exemplo de consulta HQL:

select distinct task from Task t left join fetch t.employee order by t.deadline

Nessa consulta, tudo é simples e complexo ao mesmo tempo. Vamos tentar construí-lo peça por peça.

Opção 1

Queremos baixar todos os objetostarefa, classificados por prazo. Veja como seria essa solicitação:

select task from Task t order by t.deadline

Enquanto tudo está claro. mas o campofuncionárioda classe Task conterá uma coleção de Employees anotados com a anotação EXTRA . E os objetos desta coleção não serão carregados.

opção 2

Forçar o Hibernate a carregar objetos filho para um objetotarefa.

select task from Task t join fetch t.employee order by t.deadline

Com ajuda, vinculamos explicitamente as entidades Task e Employee em nossa consulta. O Hibernate já sabe disso porque usamos as anotações @ManyToMany nesses campos.

Mas precisamos de uma instrução de junção para completá-la com uma instrução de busca para obter uma busca de junção . É assim que dizemos ao Hibernate que os objetos nas coleções Task.employee precisam ser carregados do banco de dados quando nossa solicitação é executada.

Opção 3

A solução anterior tem alguns bugs. Primeiro, depois de usar o join, o SQL não retornará objetos para nóstarefa, que não possuem objetos associados a eles na tabela Employee. É exatamente assim que funciona a junção interna .

Portanto, precisamos aumentar nossa junção com um operador esquerdo e transformá-lo em uma junção esquerda . Exemplo:

select task from Task t left join fetch t.employee order by t.deadline

Opção 4

Mas isso não é tudo. Se em seu código o relacionamento entre as entidades for de muitos para maio, haverá duplicatas nos resultados da consulta. o mesmo objetotarefapodem ser encontrados em diferentes empregados (objetos Empregado).

Portanto, você precisa adicionar a palavra-chave distinta após a palavra selecionada para se livrar do objeto Tarefa duplicado.

select distinct task from Task t left join fetch t.employee order by t.deadline

Foi assim que em 4 passos chegamos ao pedido com o qual iniciamos. Bem, o código Java parecerá bastante esperado:

String hql = " select distinct task from Task t left join fetch t.employee order by t.deadline";
Query<Task> query = session.createQuery( hql, Task.class);
return query.list();

Limitações de JOIN FETCH

Ninguém é perfeito. Instrução JOIN FETCH também. Tem algumas limitações. E a primeira é usando os métodos setMaxResults() e setFirstResult() .

Para a instrução JOIN FETCH, nosso Hibernate gerará uma consulta muito complexa na qual combinamos três tabelas em uma: funcionário, tarefa e funcionário_tarefa. Na verdade, esta não é uma solicitação para funcionários ou tarefas, mas para todos os pares de funcionários-tarefas conhecidos.

E o SQL pode aplicar suas instruções LIMIT e OFFSET exatamente a essa consulta de pares de tarefa-funcionário. Ao mesmo tempo, resulta claramente da consulta HQL que queremos obter exatamente as tarefas (Task) e, se redistribuirmos nossos parâmetros FirstResult e MaxResult, eles devem se referir especificamente aos objetos Task.

Se você escrever um código como este:

String hql = " select distinct task from Task t left join fetch t.employee order by t.deadline";
Query<Task> query = session.createQuery( hql, Task.class);
       	    query.setFirstResult(0);
        	   query.setMaxResults(1);
return query.list();

Então o Hibernate não será capaz de converter corretamente FirstResult e MaxResult para os parâmetros OFFSET e LIMIT da consulta SQL.

Em vez disso, ele fará três coisas:

  • A consulta SQL selecionará geralmente todos os dados da tabela e os retornará ao Hibernate
  • O Hibernate selecionará os registros necessários em sua memória e os retornará para você
  • O Hibernate emitirá um aviso

O aviso será algo como isto:

WARN [org.hibernate.hql.internal.ast.QueryTranslatorImpl] HHH000104: 
firstResult/maxResults specified with collection fetch; applying in memory!