Conhecendo o LazyCollectionOption.EXTRA

Mas o de maior interesse é o valor LazyCollectionOption.EXTRA. Se você especificá-lo como o valor da anotação @LazyCollection , o Hibernate atrasará o carregamento dos elementos da coleção pelo maior tempo possível.

Se você tentar obter o número de elementos em uma coleção:

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

Então, para todo esse código, o Hibernate executará apenas uma consulta:

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

No entanto, se você deseja obter um comentário da coleção, por exemplo, o número 3:

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

Então surge a pergunta: como o Hibernate deve saber que o elemento é o terceiro sem carregar todos os elementos na memória?

Para resolver este problema, propõe-se criar uma coluna adicional na tabela de comentários, que armazenará o número ordinal do comentário na coleção de comentários. E também para isso você precisa de uma anotação especial - @OrderColumn .

Veja como ficaria essa solução:

@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;
}

A principal vantagem de LazyCollectionOption.EXTRA

Vemos a maior vantagem de LazyCollectionOption.EXTRA quando o especificamos com a anotação @ManyToMany . Vamos pegar nosso antigo caso onde temos um Funcionário, uma Tarefa e podemos atribuir muitas tarefas a um usuário.

Nossas classes Java ficam assim:

Classe de funcionário :

@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>();

}

E a classe 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>();

}

E para adicionar uma tarefa ao diretor, você precisa escrever 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();

Portanto, se o campo Employees na classe Task tiver a anotação LazyCollectionOption.EXTRA, a coleção Employees (da classe Task) e a coleção Task (da classe Employee) nunca serão carregadas do banco de dados .

Quando este código for executado, apenas um registro será inserido na tabela de serviço employee_task, que, como você deve se lembrar, é mais ou menos assim:

tabela empregado_tarefa :
ID do Empregado task_id
1 1
2 2
5 3
5 4
5 5
4 7
6 8
4 101

A linha adicionada é destacada em verde. Para adicionar esta linha, você não precisa carregar coleções do banco de dados - o Hibernate fará sem isso. Este é exatamente o caso quando LazyCollectionOption.EXTRA acelera muito o trabalho com o banco de dados.

N+1 problema

Mas, claro, esse modo também tem uma desvantagem. Por exemplo, a anotação LazyCollectionOption.EXTRA gera um Problema N+1 .

Se você decidir passar por todos os comentários do usuário:

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

Então o Hibernate irá executar uma requisição separada para cada objeto Comment. E também mais uma consulta adicional para obter a contagem de todos os comentários. Isso pode tornar o código significativamente mais lento.

Se o seu usuário tiver 1000 comentários, o Hibernate fará 1001 consultas ao banco de dados para executar este código, embora possa fazer com um. Se você soubesse com antecedência que precisaria de todos os objetos desta classe.