¿Cuál es la diferencia entre JOIN y JOIN FETCH cuando se usan JPA e Hibernate?

183

Por favor, ayúdame a entender dónde usar un JOIN regular y dónde un JOIN FETCH.

Por ejemplo, si tenemos estas dos consultas

FROM Employee emp
JOIN emp.department dep

y

FROM Employee emp
JOIN FETCH emp.department dep

¿Hay alguna diferencia entre ellos? En caso afirmativo, ¿cuál usar cuando?

abbas
fuente
2
puedes encontrarlo aquí enlace leer 14.3. Asociaciones y uniones
Angga
44
He revisado esa documentación pero aún no sé dónde debo usar JOIN y dónde JOIN FETCH.
abbas
2
Si tiene el mapeo @oneToOne configurado en FetchType.LAZY y usa una segunda consulta (porque necesita que se carguen objetos de Departamento como parte de los objetos de Empleado), lo que hará Hibernate será emitir consultas para buscar objetos de Departamento para cada objeto de Empleado individual se obtiene de DB. Más adelante en el código, puede acceder a los objetos del Departamento a través de la asociación de valor único Empleado a Departamento e Hibernate no emitirá ninguna consulta para obtener el objeto Departamento para el Empleado dado. Recuerde que Hibernate sigue emitiendo consultas iguales al número de empleados que ha obtenido.
Bunti
Para ayudar en la búsqueda de documentos ~ Fetching Strategies
Eddie B
1
@ShameeraAnuranga Creo que en ese caso necesitarás una IZQUIERDA EXTERIOR ÚNICA.
abbas

Respuestas:

180

En estas dos consultas, está utilizando JOIN para consultar a todos los empleados que tienen al menos un departamento asociado.

Pero, la diferencia es: en la primera consulta, solo está devolviendo los Empleados para Hibernate. En la segunda consulta, está devolviendo los Empleados y todos los Departamentos asociados.

Por lo tanto, si usa la segunda consulta, no necesitará hacer una nueva consulta para volver a acceder a la base de datos para ver los departamentos de cada empleado.

Puede usar la segunda consulta cuando esté seguro de que necesitará el Departamento de cada Empleado. Si no necesita el Departamento, use la primera consulta.

Recomiendo leer este enlace si necesita aplicar alguna condición WHERE (lo que probablemente necesitará): ¿Cómo expresar adecuadamente JPQL "join fetch" con la cláusula "where" como JPA 2 CriteriaQuery?

Actualizar

Si no usa fetchy los departamentos continúan devolviéndose, es porque su asignación entre Empleado y Departamento (a @OneToMany) está configurada con FetchType.EAGER. En este caso, cualquier consulta HQL (con fetcho no) con FROM Employeetraerá todos los departamentos. Recuerde que todas las asignaciones * ToOne ( @ManyToOney @OneToOne) son EAGER por defecto.

Dherik
fuente
1
Qué comportamiento será si ejecutamos una instrucción sin recuperación y obtenemos resultados. ¿Entonces dentro de la sesión trataremos al departamento?
gstackoverflow
1
@gstackoverflow, sí
Dherik 01 de
Utilizo una consulta nativa con Lazy fetch en ambos lados de la relación, pero aún carga la jerarquía de las relaciones secundarias.
Badamchi
Vale la pena mencionar que fetchdebe usarse si (usando nuestro ejemplo) desea ordenar por algún atributo de Departamento. De lo contrario, (válido al menos para PG) que podría obtenerERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
de largo
60

en este enlace que mencioné antes en el comentario, lea esta parte:

Una unión "fetch" permite inicializar asociaciones o colecciones de valores junto con sus objetos principales mediante una única selección. Esto es particularmente útil en el caso de una colección. Anula de manera efectiva las uniones externas y las declaraciones diferidas del archivo de mapeo para asociaciones y colecciones.

este "JOIN FETCH" tendrá efecto si tiene la propiedad (fetch = FetchType.LAZY) para una colección dentro de la entidad (ejemplo a continuación).

Y solo tiene efecto el método de "cuándo debe ocurrir la consulta". Y también debes saber esto :

Hibernate tiene dos nociones ortogonales: cuándo se obtiene la asociación y cómo se obtiene. Es importante que no los confunda. Utilizamos fetch para ajustar el rendimiento. Podemos usar perezoso para definir un contrato para qué datos siempre están disponibles en cualquier instancia separada de una clase en particular.

cuándo se obtiene la asociación -> su tipo "FETCH"

cómo se obtiene -> Unir / seleccionar / Subseleccionar / Lote

En su caso, FETCH solo tendrá efecto si tiene un departamento como conjunto dentro de Employee, algo así en la entidad:

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

cuando usas

FROM Employee emp
JOIN FETCH emp.department dep

obtendrás empy emp.dep. cuando no usó fetch todavía puede obtener, emp.deppero hibernate procesará otra selección en la base de datos para obtener ese conjunto de departamentos.

así que es solo una cuestión de ajuste de rendimiento, acerca de si desea obtener todos los resultados (lo necesita o no) en una sola consulta (búsqueda ansiosa), o desea consultarlo más tarde cuando lo necesite (recuperación perezosa).

Use la búsqueda entusiasta cuando necesite obtener datos pequeños con una selección (una consulta grande). O use la recuperación lenta para consultar lo que necesita más tarde (muchas consultas más pequeñas).

use fetch cuando:

  • no hay una gran colección / conjunto innecesario dentro de esa entidad que está a punto de obtener

  • comunicación del servidor de aplicaciones al servidor de la base de datos demasiado lejos y necesita mucho tiempo

  • es posible que necesite esa colección más tarde cuando no tiene acceso a ella ( fuera del método / clase transaccional )

Angga
fuente
¿Podría explicarlo por las consultas que acabo de escribir en la pregunta actualizada?
abbas
consideración útil: "no hay una gran colección / conjunto innecesario dentro de esa entidad que está a punto de obtener"
Divs
¿Se buscarían ansiosamente los departamentos si los departamentos dentro del empleado fueran un en Listlugar de un Set?
Stephane
¿El uso de la FETCHpalabra clave en una declaración JPQL implica una propiedad recuperada con entusiasmo?
Stephane
15

UNIRSE

Cuando se utiliza JOINcontra las asociaciones de una entidad, JPA generará una UNIÓN entre la entidad principal y las tablas de la entidad secundaria en la instrucción SQL generada.

Entonces, tomando su ejemplo, al ejecutar esta consulta JPQL:

FROM Employee emp
JOIN emp.department dep

Hibernate va a generar la siguiente instrucción SQL:

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

Tenga en cuenta que la SELECTcláusula SQL contiene solo las employeecolumnas de la tabla, y no departmentlas. Para buscar las departmentcolumnas de la tabla, necesitamos usar en JOIN FETCHlugar de JOIN.

ÚNETE A FETCH

Entonces, en comparación con JOIN, le JOIN FETCHpermite proyectar las columnas de la tabla de unión en la SELECTcláusula de la instrucción SQL generada.

Entonces, en su ejemplo, al ejecutar esta consulta JPQL:

FROM Employee emp
JOIN FETCH emp.department dep

Hibernate va a generar la siguiente instrucción SQL:

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

Tenga en cuenta que, esta vez, las departmentcolumnas de la tabla también se seleccionan, no solo las asociadas con la entidad enumerada en la FROMcláusula JPQL.

Además, JOIN FETCHes una excelente manera de abordar el problema LazyInitializationExceptional utilizar Hibernate, ya que puede inicializar asociaciones de entidades utilizando la FetchType.LAZYestrategia de recuperación junto con la entidad principal que está recuperando.

Vlad Mihalcea
fuente
¿Es posible usar múltiples JOIN FETCH en la misma consulta?
A.Onur Özcan
2
Puede UNIRSE A FETCH múltiples asociaciones muchos a uno y uno a uno y, como máximo, una colección. Obtener múltiples colecciones, como asociaciones de uno a muchos o de muchos a muchos, terminará en un producto cartesiano . Sin embargo, si desea obtener varias colecciones, puede usar consultas secundarias para la segunda, tercera, ..., nésimas colecciones. Mira este artículo para más detalles.
Vlad Mihalcea
5

Si tiene @oneToOneconfigurado el mapeo FetchType.LAZYy usa una segunda consulta (porque necesita que los objetos de Departamento se carguen como parte de los objetos de Empleado), lo que Hibernate hará es emitir consultas para recuperar objetos de Departamento para cada objeto de Empleado individual que recupere de DB.

Más tarde, en el código, puede acceder a los objetos del Departamento a través de la asociación de un solo valor Empleado a Departamento e Hibernate no emitirá ninguna consulta para obtener el objeto Departamento para el Empleado dado.

Recuerde, Hibernate sigue emitiendo consultas iguales al número de empleados que ha obtenido. Hibernate emitirá el mismo número de consultas en las dos consultas anteriores, si desea acceder a los objetos del Departamento de todos los objetos de Empleado

Bunti
fuente
2

Dherik: No estoy seguro de lo que dices, cuando no uses fetch, el resultado será de tipo: lo List<Object[ ]>que significa una lista de tablas de Objetos y no una lista de Empleados.

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

Cuando usa fetch, solo hay una selección y el resultado es la lista de Empleados que List<Employee>contiene la lista de departamentos. Anula la declaración perezosa de la entidad.

Bilal BBB
fuente
No sé si entiendo tu preocupación. Si no lo usa fetch, su consulta devolverá solo los empleados. Si los departamentos, incluso en este caso, continúan siendo devueltos, es porque su asignación entre Empleado y Departamento (un @OneToMany) está configurada con FetchType.EAGER. En este caso, cualquier consulta HQL (con fetcho no) con FROM Employeetraerá todos los departamentos.
Dherik
Sin usar fetch (solo término de unión), el resultado sería una matriz de colecciones, dos filas, la primera es una colección de Empleados y la segunda es una colección de Departamentos. Usando la búsqueda ansiosa o la recuperación perezosa, se buscarán los departamentos.
Bilal BBB
Sin buscar en HQL, esto sucederá solo si su mapeo entre Empleado y Departamento es EAGER ( @OneToMany(fetch = FetchType.EAGER). Si no es el caso, los departamentos no serán devueltos.
Dherik
@Dherik pruébalo tú mismo, obtendrás una ClassCastException.
Bilal BBB
Me doy cuenta del problema. No es un problema de búsqueda, sino cómo selectse hizo en HQL. Tratar SELECT emp FROM Employee emp JOIN FETCH emp.department dep. JPA / Hibernate tiene este comportamiento de retorno de una Listde Object[]cuando ommit la SELECTparte.
Dherik