Descripción del problema

Como dijimos anteriormente, la anotación LazyCollectionOption.EXTRA tiene un problema: realiza una solicitud separada a la base de datos para cada objeto. Necesitamos explicar de alguna manera a Hibernate que queremos que cargue inmediatamente todos los objetos secundarios para nuestros objetos principales.

Los desarrolladores de Hibernate han encontrado una solución a este problema, el operador join fetch en HQL.

Ejemplo de consulta HQL:

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

En esta consulta todo es simple y complejo a la vez. Intentemos construirlo pieza por pieza.

Opción 1

Queremos descargar todos los objetos.tarea, ordenados por fecha límite. Así es como se vería esa solicitud:

select task from Task t order by t.deadline

Mientras todo está claro. pero el campoempleadode la clase Tarea contendrá una colección de Empleados anotados con la anotación EXTRA . Y los objetos de esta colección no se cargarán.

opcion 2

Forzar a Hibernate a cargar objetos secundarios para un objetotarea.

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

Con ayuda, vinculamos explícitamente las entidades Tarea y Empleado en nuestra consulta. Hibernate ya sabe esto porque usamos las anotaciones @ManyToMany en estos campos.

Pero necesitamos una declaración de unión para completarla con una declaración de búsqueda para obtener una búsqueda de unión . Así es como le decimos a Hibernate que los objetos en las colecciones Task.employee deben cargarse desde la base de datos cuando se ejecuta nuestra solicitud.

Opción 3

La solución anterior tiene algunos errores. Primero, después de usar join, SQL no nos devolverá objetostarea, que no tienen objetos asociados con ellos en la tabla Empleado. Así es exactamente como funciona la unión interna .

Así que necesitamos aumentar nuestra combinación con un operador izquierdo y convertirlo en una combinación izquierda . Ejemplo:

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

Opción 4

Pero eso no es todo. Si en su código la relación entre entidades es de muchos a mayo, habrá duplicados en los resultados de la consulta. el mismo objetotarease pueden encontrar en diferentes empleados (objetos de empleados).

Por lo tanto, debe agregar la palabra clave distinta después de la palabra seleccionada para deshacerse del objeto Tarea duplicado.

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

Así fue como en 4 pasos llegamos al pedido con el que empezamos. Bueno, el código Java se verá 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();

Limitaciones de JOIN FETCH

Nadie es perfecto. declaración JOIN FETCH también. Tiene bastantes limitaciones. Y el primero está usando los métodos setMaxResults() y setFirstResult() .

Para la declaración JOIN FETCH, nuestro Hibernate generará una consulta muy compleja en la que combinamos tres tablas en una: empleado, tarea y empleado_tarea. De hecho, no se trata de una solicitud de empleados o tareas, sino de todos los pares empleados-tarea conocidos.

Y SQL puede aplicar sus sentencias LIMIT y OFFSET exactamente a esa consulta de pares empleado-tarea. Al mismo tiempo, de la consulta HQL se deduce claramente que queremos obtener exactamente tareas (Tarea), y si redistribuimos nuestros parámetros FirstResult y MaxResult, entonces deberían referirse específicamente a objetos Tarea.

Si escribes 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();

Entonces Hibernate no podrá convertir correctamente FirstResult y MaxResult a los parámetros OFFSET y LIMIT de la consulta SQL.

En cambio, hará tres cosas:

  • La consulta SQL seleccionará generalmente todos los datos de la tabla y los devolverá a Hibernate
  • Hibernate seleccionará los registros necesarios en su memoria y se los devolverá
  • Hibernate emitirá una advertencia

La advertencia será algo como esto:

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