Qual é a diferença entre JOIN e JOIN FETCH ao usar JPA e Hibernate

183

Ajude-me a entender onde usar um JOIN regular e onde um JOIN FETCH.

Por exemplo, se tivermos essas duas consultas

FROM Employee emp
JOIN emp.department dep

e

FROM Employee emp
JOIN FETCH emp.department dep

Existe alguma diferença entre eles? Se sim, qual usar quando?

abbas
fonte
2
você pode encontrá-lo aqui ligar ler 14.3. Associações e associações
Angga
4
Passei por essa documentação, mas ainda não sei onde devo usar um JOIN e onde um JOIN FETCH.
abbas
2
Se você tiver o mapeamento @oneToOne definido como FetchType.LAZY e usar a segunda consulta (porque você precisa que os objetos de Departamento sejam carregados como parte dos objetos Employee), o que o Hibernate fará é, ele emitirá consultas para buscar objetos de Departamento para cada objeto Employee individual busca do DB. Posteriormente no código, você poderá acessar os objetos Department via associação de valor único Employee to Department e o Hibernate não emitirá nenhuma consulta para buscar o objeto Department para o Employee em questão. Lembre-se de que o Hibernate ainda emite consultas iguais ao número de funcionários que buscou.
Bunti
Para ajudar na caça doc ~ Estratégias de Fetching
Eddie B
1
@ ShameeraAnuranga Acho que, nesse caso, você precisará de uma junção externa esquerda.
abbas

Respostas:

180

Nessas duas consultas, você está usando JOIN para consultar todos os funcionários que possuem pelo menos um departamento associado.

Mas a diferença é: na primeira consulta, você está retornando apenas os funcionários do Hibernate. Na segunda consulta, você está retornando os Empregados e todos os Departamentos associados.

Portanto, se você usar a segunda consulta, não precisará fazer uma nova consulta para acessar o banco de dados novamente e ver os Departamentos de cada Funcionário.

Você pode usar a segunda consulta quando tiver certeza de que precisará do departamento de cada funcionário. Se você não precisar do departamento, use a primeira consulta.

Eu recomendo a leitura deste link se você precisar aplicar alguma condição WHERE (o que você provavelmente precisará): Como expressar corretamente o JPQL "junção de busca" com a cláusula "where" como JPA 2 CriteriaQuery?

Atualizar

Se você não usar fetche os departamentos continuarem sendo devolvidos, é porque o seu mapeamento entre Funcionário e Departamento (a @OneToMany) está definido FetchType.EAGER. Nesse caso, qualquer fetchconsulta HQL (com ou não) FROM Employeetrará todos os departamentos. Lembre-se de que todo o mapeamento * ToOne ( @ManyToOnee @OneToOne) é EAGER por padrão.

Dherik
fonte
1
Qual comportamento será se executarmos a instrução sem buscar e obter resultado. Então, dentro da sessão, trataremos o departamento?
Gtackoverflow
1
@gstackoverflow, yes #
Dherik
Eu uso a consulta nativa com busca preguiçosa nos dois lados do relacionamento, mas ainda carrega a hierarquia das relações filho.
Badamchi 25/12/19
Vale mencionar que fetchdeve ser usado se (usando o nosso exemplo) você desejar solicitar por algum atributo do Departamento. Caso contrário, (válido pelo menos para o PG), você poderá obterERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
longo
60

em este link eu mencionei antes sobre o comentário, leia esta parte:

Uma junção "buscar" permite que associações ou coleções de valores sejam inicializadas junto com seus objetos pai usando uma única seleção. Isso é particularmente útil no caso de uma coleção. Ele efetivamente substitui as declarações de junção externa e lenta do arquivo de mapeamento para associações e coleções.

este "JOIN FETCH" terá efeito se você tiver (fetch = FetchType.LAZY) a propriedade de uma coleção dentro da entidade (exemplo a seguir).

E é apenas o método de "quando a consulta deve acontecer". E você também deve saber isso :

O hibernate possui duas noções ortogonais: quando a associação é buscada e como é buscada. É importante que você não os confunda. Usamos a busca para ajustar o desempenho. Podemos usar o lazy para definir um contrato para quais dados estão sempre disponíveis em qualquer instância desanexada de uma classe específica.

quando a associação é buscada -> seu tipo "FETCH"

como é buscado -> Participar / selecionar / Subselecionar / Lote

No seu caso, o FETCH só terá efeito se você tiver um departamento como um conjunto dentro de Employee, algo parecido com isto na entidade:

@OneToMany(fetch = FetchType.LAZY)
private Set<Department> department;

quando você usa

FROM Employee emp
JOIN FETCH emp.department dep

você receberá empe emp.dep. quando você não usou a busca, ainda é possível obter, emp.depmas o hibernate processará outra seleção no banco de dados para obter esse conjunto de departamentos.

portanto, é apenas uma questão de ajuste de desempenho: você deseja obter todos os resultados (você precisa ou não) em uma única consulta (busca ansiosa) ou deseja consultá-lo mais tarde quando precisar (busca lenta).

Use a busca ansiosa quando precisar obter pequenos dados com uma seleção (uma grande consulta). Ou use a busca lenta para consultar o que você precisa depois (muitas consultas menores).

use buscar quando:

  • nenhuma grande coleção / conjunto desnecessário dentro dessa entidade que você está prestes a receber

  • comunicação do servidor de aplicativos com o servidor de banco de dados longe demais e precisa de muito tempo

  • você pode precisar dessa coleção posteriormente quando não tiver acesso a ela ( fora do método / classe transacional )

Angga
fonte
Você poderia explicar isso para as perguntas que acabei de escrever na pergunta atualizada.
abbas
consideração útil: "nenhuma grande coleção / conjunto desnecessário dentro dessa entidade que você está prestes a obter"
Divs 30/01/18
Os departamentos ainda seriam avidamente buscados se os departamentos dentro do funcionário fossem um em Listvez de um Set?
Stephane
O uso da FETCHpalavra - chave em uma instrução JPQL implica uma propriedade recuperada ansiosamente?
Stephane
15

JUNTE-SE

Ao usar JOINcontra associações de uma entidade, o JPA gerará uma JOIN entre as tabelas da entidade pai e da entidade filha na instrução SQL gerada.

Então, tomando o seu exemplo, ao executar esta consulta JPQL:

FROM Employee emp
JOIN emp.department dep

O Hibernate irá gerar a seguinte instrução SQL:

SELECT emp.*
FROM employee emp
JOIN department dep ON emp.department_id = dep.id

Observe que a SELECTcláusula SQL contém apenas as employeecolunas da tabela, e não departmentas. Para buscar as departmentcolunas da tabela, precisamos usar em JOIN FETCHvez de JOIN.

JUNTE-SE À PESQUISA

Portanto, em comparação com JOIN, o JOIN FETCHpermite projetar as colunas da tabela de junção na SELECTcláusula da instrução SQL gerada.

Portanto, no seu exemplo, ao executar esta consulta JPQL:

FROM Employee emp
JOIN FETCH emp.department dep

O Hibernate irá gerar a seguinte instrução SQL:

SELECT emp.*, dept.*
FROM employee emp
JOIN department dep ON emp.department_id = dep.id

Observe que, desta vez, as departmentcolunas da tabela também são selecionadas, não apenas as associadas à entidade listada na FROMcláusula JPQL.

Além disso, JOIN FETCHé uma ótima maneira de abordar a questão LazyInitializationExceptionao usar o Hibernate, pois você pode inicializar associações de entidades usando a FetchType.LAZYestratégia de busca junto com a entidade principal que você está buscando.

Vlad Mihalcea
fonte
É possível usar vários JOIN FETCH na mesma consulta?
A.Onur Özcan
2
Você pode participar de várias associações muitos-para-um e um-para-um e no máximo uma coleção. A busca de várias coleções, como associações um para muitos ou muitos para muitos, terminará em um produto cartesiano . No entanto, se você deseja buscar várias coleções, pode usar consultas secundárias para a segunda, terceira, ..., enésima coleção. Confira este artigo para mais detalhes.
Vlad Mihalcea
5

Se você tiver o @oneToOnemapeamento definido FetchType.LAZYe usar a segunda consulta (porque você precisa que os objetos do Departamento sejam carregados como parte dos objetos Employee), o que o Hibernate fará é, ele emitirá consultas para buscar objetos do Department para cada objeto Employee individual que busca no DB.

Posteriormente, no código, você poderá acessar os objetos Department por meio da associação de valor único Employee to Department e o Hibernate não emitirá nenhuma consulta para buscar o objeto Department para o Employee em questão.

Lembre-se, o Hibernate ainda emite consultas iguais ao número de funcionários que buscou. O Hibernate emitirá o mesmo número de consultas nas duas perguntas acima, se você desejar acessar os objetos de departamento de todos os objetos de funcionários

Bunti
fonte
2

Dherik: Não tenho certeza do que você diz, quando você não usa buscar, o resultado será do tipo: o List<Object[ ]>que significa uma lista de tabelas de objetos e não uma lista de funcionários.

Object[0] refers an Employee entity 
Object[1] refers a Departement entity 

Quando você usa a busca, há apenas uma seleção e o resultado é a lista de Funcionários que List<Employee>contém a lista de departamentos. Substitui a declaração lenta da entidade.

Bilal BBB
fonte
Não sei se entendi sua preocupação. Se você não usar fetch, sua consulta retornará apenas os funcionários. Se os departamentos, mesmo nesse caso, continuarem a ser devolvidos, é porque o mapeamento entre Funcionário e Departamento (um @OneToMany) está definido com FetchType.EAGER. Nesse caso, qualquer fetchconsulta HQL (com ou não) FROM Employeetrará todos os departamentos.
Dherik
Sem usar a busca (apenas o termo de associação), o resultado seria uma matriz de coleções, duas linhas, a primeira é uma coleção de Funcionários e a segunda é uma coleção de Departamentos. Usando busca ansiosa ou busca lenta, os departamentos serão buscados.
Bilal BBB
Sem buscar no HQL, isso acontecerá apenas se o seu mapeamento entre Funcionário e Departamento for EAGER ( @OneToMany(fetch = FetchType.EAGER). Caso contrário, os departamentos não serão devolvidos.
Dherik
@ Dherik tente você mesmo, você receberá uma ClassCastException.
Bilal BBB
Eu descobri o problema. Não é um problema de busca, mas como isso selectfoi feito no HQL. Tente SELECT emp FROM Employee emp JOIN FETCH emp.department dep. JPA / Hibernate tem esse comportamento de retornar um Listde Object[]quando você omite a SELECTpeça.
Dherik