Conociendo LazyCollectionOption.EXTRA

Pero de mayor interés es el valor LazyCollectionOption.EXTRA. Si lo especifica como el valor de la anotación @LazyCollection , Hibernate retrasará la carga de los elementos de la colección tanto como sea posible.

Si intenta obtener el número de elementos en una colección:

User user = session.load(User.class, 1);
List<Comment> comments = user.getComments();
int count = commetns.size();

Luego, para todo este código, Hibernate ejecutará solo una consulta:

SELECT COUNT(id) FROM comment WHERE user_id = 1;

Sin embargo, si desea obtener un comentario de la colección, por ejemplo, el número 3:

User user = session.load(User.class, 1);
List<Comment> comments = user.getComments();
Comment comment = commetns.get(3);

Entonces surge la pregunta: ¿cómo se supone que Hibernate sepa que el elemento es el tercero sin cargar todos los elementos en la memoria?

Para solucionar este problema, se propone hacer una columna adicional en la tabla de comentarios, que almacenará el número ordinal del comentario en la colección de comentarios. Y también para esto necesita una anotación especial: @OrderColumn .

Así es como se vería esa solución:

@Entity
@Table(name=”user”)
class User {
   @Column(name=”id”)
   public Integer id;

   @OneToMany(cascade = CascadeType.ALL)
   @LazyCollection(LazyCollectionOption.EXTRA)
   @OrderColumn(name = "order_id")
   public List<Comment> comments;
}

La principal ventaja de LazyCollectionOption.EXTRA

Vemos la mayor ventaja de LazyCollectionOption.EXTRA cuando lo especificamos con la anotación @ManyToMany . Tomemos nuestro caso anterior en el que tenemos un empleado, una tarea y podemos asignar muchas tareas a un usuario.

Nuestras clases de Java se ven así:

Clase de empleado :

@Entity
@Table(name=”employee”)
class Employee {
   @Column(name=”id”)
   public Integer id;

   @ManyToMany(cascade = CascadeType.ALL)
   @JoinTable(name="employee_task",
       	joinColumns=  @JoinColumn(name="employee_id", referencedColumnName="id"),
       	inverseJoinColumns= @JoinColumn(name="task_id", referencedColumnName="id") )
   @LazyCollection(LazyCollectionOption.EXTRA)
   private Set<EmployeeTask> tasks = new HashSet<EmployeeTask>();

}

Y la clase EmployeeTask :

@Entity
@Table(name=”task”)
class EmployeeTask {
   @Column(name=”id”)
   public Integer id;

   @ManyToMany(cascade = CascadeType.ALL)
   @JoinTable(name="employee_task",
       	joinColumns=  @JoinColumn(name="task_id", referencedColumnName="id"),
       	inverseJoinColumns= @JoinColumn(name=" employee_id", referencedColumnName="id") )
   @LazyCollection(LazyCollectionOption.EXTRA)
   private Set<Employee> employees = new HashSet<Employee>();

}

Y para agregar una tarea al director, debe escribir algo como este código:

Employee director = session.find(Employee.class, 4);
EmployeeTask task = session.find(EmployeeTask.class, 101);
task.employees.add(director);

session.update(task);
session.flush();

Por lo tanto, si el campo de empleados en la clase Tarea tiene la anotación LazyCollectionOption.EXTRA, entonces la colección de empleados (de la clase Tarea) y la colección de tareas (de la clase Empleado) nunca se cargarán desde la base de datos .

Cuando se ejecuta este código, solo se insertará un registro en la tabla de servicio employee_task, que, como recordará, se parece a esto:

tabla empleado_tarea :
ID de empleado ID_tarea
1 1
2 2
5 3
5 4
5 5
4 7
6 8
4 101

La línea añadida se resalta en verde. Para agregar esta línea, no necesita cargar colecciones desde la base de datos; Hibernate lo hará sin ella. Este es exactamente el caso cuando LazyCollectionOption.EXTRA acelera enormemente el trabajo con la base de datos.

problema n+1

Pero, por supuesto, este modo también tiene un inconveniente. Por ejemplo, la anotación LazyCollectionOption.EXTRA genera un problema N+1 .

Si decide revisar todos los comentarios de sus usuarios:

User user = session.load(User.class, 1);
List<Comment> comments = user.getComments();
for (Comment comment : comments) {
    System.out.println(comment);
}

Entonces Hibernate se ejecutará en una solicitud separada para cada objeto Comentario. Y también una consulta adicional más para obtener el recuento de todos los comentarios. Esto puede ralentizar significativamente el código.

Si su usuario tiene 1000 comentarios, entonces Hibernate hará 1001 consultas a la base de datos para ejecutar este código, aunque le vendría bien una. Si supiera de antemano que necesitaría todos los objetos de esta clase.