Conoscere LazyCollectionOption.EXTRA

Ma di maggiore interesse è il valore LazyCollectionOption.EXTRA. Se lo specifichi come valore dell'annotazione @LazyCollection , Hibernate ritarderà il caricamento degli elementi della raccolta il più a lungo possibile.

Se provi a ottenere il numero di elementi in una raccolta:

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

Quindi per tutto questo codice, Hibernate eseguirà solo una query:

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

Tuttavia, se desideri ottenere un commento dalla raccolta, ad esempio il numero 3:

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

Quindi sorge la domanda: come fa Hibernate a sapere che l'elemento è il terzo senza caricare tutti gli elementi in memoria?

Per risolvere questo problema, si propone di creare una colonna aggiuntiva nella tabella dei commenti, che memorizzerà il numero ordinale del commento nella raccolta dei commenti. E anche per questo hai bisogno di un'annotazione speciale - @OrderColumn .

Ecco come sarebbe questa soluzione:

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

Il vantaggio principale di LazyCollectionOption.EXTRA

Vediamo il vantaggio più forte di LazyCollectionOption.EXTRA quando lo specifichiamo con l' annotazione @ManyToMany . Prendiamo il nostro vecchio caso in cui abbiamo un dipendente, un'attività e possiamo assegnare molte attività a un utente.

Le nostre classi Java hanno questo aspetto:

Classe dipendente :

@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 la 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 per aggiungere un'attività al direttore, devi scrivere qualcosa come questo codice:

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

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

Quindi, se il campo employee nella classe Task ha l'annotazione LazyCollectionOption.EXTRA, la collection employee (della classe Task) e la collection task (della classe Employee) non verranno mai caricate dal database .

Quando questo codice viene eseguito, solo un record verrà inserito nella tabella di servizio employee_task, che, come ricorderete, è simile a questa:

tabella employee_task :
ID Dipendente task_id
1 1
2 2
5 3
5 4
5 5
4 7
6 8
4 101

La linea aggiunta è evidenziata in verde. Per aggiungere questa riga, non è necessario caricare raccolte dal database: Hibernate ne farà a meno. Questo è esattamente il caso in cui LazyCollectionOption.EXTRA accelera notevolmente il lavoro con il database.

Problema N+1

Ma, ovviamente, questa modalità ha anche uno svantaggio. Ad esempio, l'annotazione LazyCollectionOption.EXTRA genera un N+1 Problem .

Se decidi di esaminare tutti i commenti dei tuoi utenti:

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

Quindi Hibernate verrà eseguito su una richiesta separata per ciascun oggetto Comment. E anche un'altra query aggiuntiva per ottenere il conteggio di tutti i commenti. Questo può rallentare notevolmente il codice.

Se il tuo utente ha 1000 commenti, Hibernate effettuerà 1001 query al database per eseguire questo codice, anche se potrebbe farne uno. Se sapessi in anticipo che avresti bisogno di tutti gli oggetti di questa classe.