Differenza tra Eager e Lazy: come popolare entità con Hibernate


Scopri le potenzialità di Hibernate per Java e quanto possa esserti utile nella manipolazione dei dati e della gestione dei database.
di Antonio Lamorgese


Come ben sai, Hibernate è un ORM, cioè una libreria di classi JAVA che associa i campi di un record di una tabella a degli oggetti chiamati Entità. Il sistema di associazione dei valori di questi campi agli oggetti può avvenire in due modi, EAGER e LAZY sono i due meccanismi previsti per caricare oggetti Entità.

Il tipo di caricamento, cioè EAGER e LAZY, può essere specificato su un campo di un Entità (cioè la classe che rappresenta un intero record con tutti i campi correlati) attraverso l’annotation, @basic, mentre su un campo che rappresenta una relazione attraverso le annotation di relazione e cioè: @OneToOne, @OneToMany, ManyToMany o @ManyToOne.

Quindi per realizzare il caricamento di tipo LAZY su un campo, utilizziamo l’attributo fetch presente nell’annotation @Basic come mostrato nell’esempio:

@Column(name="COGNOME")
@Basic(fetch=FetchType.LAZY)
public String getCognome(){   
   return cognome;
}..

Ma come facciamo a sapere quando specificare LAZY e quando EAGER? beh Hibernate in assenza di una impostazione esplicita da parte del programmatore imposta per default il tipo di caricamento secondo questa tabella:

Basic: LAZY
OneToOne: EAGER
ManyToOne: EAGER
OneToMany: LAZY
ManyToMany: LAZY

Per cui quando abbiamo dei dubbi su come impostare la tipologia di caricamento piuttosto di associare un meccanismo errato di prelevamento dei dati di un campo, possiamo lasciar decidere ad Hibernate quale usare, fidatevi è senz’altro la scelta migliore, quindi non impostate l’attributo fetch e lasciate decidere ad Hibernate, in quanto impostare un meccanismo errato di prelevamento dei dati può penalizzare pesantemente le prestazioni del software.

Ad ogni modo ora ti mostrerò come mappare entità con Hibernate.

1. I concetti chiave di Hibernate

Uno dei concetti chiave di Hibernate è l’uso di entità per rappresentare i dati del database. Ora, esploreremo nel dettaglio come Hibernate gestisce le entità e come è possibile utilizzare il concetto di eager e lazy loading per migliorare le prestazioni delle nostre applicazioni.

Prima di iniziare, è importante capire cosa sono le entità in Hibernate. Come ti ho già annunciato poc’anzi un’entità è una classe Java che rappresenta una tabella del database. Ogni istanza di un’entità rappresenta una riga della tabella e ogni attributo dell’entità rappresenta una colonna della tabella.

Ad esempio, supponiamo di avere una tabella “employees” nel nostro database con colonne come “id”, “name”, “age”, “salary” e così via. Possiamo creare un’entità Employee in Hibernate per rappresentare questa tabella come segue:

@Entity
@Table(name = "employees")
public class Employee {
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   @Column(name = "id")
   private Long id;
   @Column(name = "name")
   private String name;
   @Column(name = "age")
   private int age;
   @Column(name = "salary")
   private double salary;
   // costruttori, getter e setter
}

In questo esempio, ho utilizzato le annotazioni di Hibernate per mappare la classe Employee alla tabella “employees”. L’annotazione @Entity indica che questa classe è un’entità e l’annotazione @Table indica il nome della tabella nel database. L’annotazione @Id indica che l’attributo id è la chiave primaria dell’entità e l’annotazione @GeneratedValue indica che il valore dell’attributo id verrà generato automaticamente dal database.

Ora che abbiamo creato la nostra entità Employee, possiamo utilizzarla per interagire con i dati della tabella “employees” nel nostro database. Tuttavia, una delle sfide nell’utilizzo di entità in Hibernate è il caricamento dei dati associati.

Il caricamento dei dati associati si riferisce al modo in cui Hibernate recupera i dati dalle tabelle correlate durante la lettura di un’entità. Ci sono due modi principali per gestire il caricamento dei dati associati in Hibernate: eager e lazy loading.

2. Eager loading

L’eager loading si riferisce al caricamento di tutti i dati associati di un’entità al momento della sua lettura. Ad esempio, se abbiamo un’entità Employee con una relazione ManyToOne a un’altra entità Department, possiamo utilizzare eager loading per caricare l’intero dipartimento insieme all’entità Employee come segue:

@Entity
@Table(name = "employees")
public class Employee {
   // attributi come prima
   @ManyToOne(fetch = FetchType.EAGER)
   @JoinColumn(name = "department_id")
   private Department department;
   // costruttori, getter e setter
}
@Entity
@Table(name = "departments")
public class Department {
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   @Column(name = "id")
   private Long id;
   @Column(name = "name")
   private String name;
   // costruttori, getter e setter
}

In questo esempio, ho aggiunto un’annotazione @ManyToOne all’attributo department dell’entità Employee per indicare che si tratta di una relazione ManyToOne con l’entità Department. Abbiamo anche specificato fetch = FetchType.EAGER per indicare che vogliamo caricare il dipartimento insieme all’entità Employee.

Tuttavia, l’uso di eager loading può portare a problemi di prestazioni se l’entità ha molte relazioni o se le relazioni hanno molte righe. Ciò può causare un aumento del tempo di caricamento dei dati e un consumo di memoria elevato.

3. Lazy loading

In alternativa, possiamo utilizzare il lazy loading per caricare i dati associati solo quando sono richiesti. Ad esempio, possiamo utilizzare lazy loading per caricare il dipartimento solo quando viene chiamato il suo getter come segue:

@Entity
@Table(name = "employees")
public class Employee {
   // attributi come prima
   @ManyToOne(fetch = FetchType.LAZY)
   @JoinColumn(name = "department_id")
   private Department department;
   // costruttori, getter e setter
}

In questo esempio, ho utilizzato fetch = FetchType.LAZY per indicare che vogliamo caricare il dipartimento solo quando viene chiamato il suo getter. Ciò significa che il dipartimento non verrà caricato insieme all’entità Employee durante la sua lettura, ma solo quando il suo getter viene chiamato per la prima volta.

L’utilizzo del lazy loading può migliorare le prestazioni delle nostre applicazioni riducendo il tempo di caricamento dei dati e il consumo di memoria. Tuttavia, è importante essere consapevoli delle possibili eccezioni che possono verificarsi quando si utilizza il lazy loading.

Ad esempio, se si tenta di accedere a un’entità associata che non è stata caricata, Hibernate lancerà un’eccezione LazyInitializationException. Per evitare questo problema, è possibile utilizzare la sessione di Hibernate per caricare esplicitamente i dati associati prima di chiudere la transazione.

Ad esempio, supponiamo di avere un’entità Employee con una relazione OneToMany a un’altra entità Project come segue:

@Entity
@Table(name = "employees")
public class Employee {
   // attributi come prima
   @OneToMany(mappedBy = "employee", fetch = FetchType.LAZY)
   private List<Project> projects;
   // costruttori, getter e setter
}
@Entity
@Table(name = "projects")
public class Project {
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   @Column(name = "id")
   private Long id;
   @Column(name = "name")
   private String name;
   @ManyToOne(fetch = FetchType.LAZY)
   @JoinColumn(name = "employee_id")
   private Employee employee;
   // costruttori, getter e setter
}

In questo esempio, ho aggiunto un’annotazione @OneToMany all’attributo projects dell’entità Employee per indicare che si tratta di una relazione OneToMany con l’entità Project. Abbiamo anche specificato fetch = FetchType.LAZY per indicare che vogliamo caricare i progetti solo quando sono richiesti.

Tuttavia, se si tenta di accedere alla lista dei progetti di un’entità Employee dopo che la transazione è stata chiusa, Hibernate lancerà un’eccezione LazyInitializationException. Per evitare questo problema, possiamo utilizzare la sessione di Hibernate per caricare esplicitamente i progetti prima di chiudere la transazione come segue:

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
Employee employee = session.get(Employee.class, employeeId);
Hibernate.initialize(employee.getProjects());
tx.commit();
session.close();

In questo esempio, ho utilizzato il metodo Hibernate.initialize per caricare esplicitamente i progetti dell’entità Employee prima di chiudere la transazione. Ciò garantisce che i dati associati siano stati caricati correttamente e che non verrà lanciata alcuna eccezione LazyInitializationException.

4. Conclusioni

In conclusione, il concetto di eager e lazy loading è un aspetto importante da considerare quando si utilizzano le entità in Hibernate. L’uso di eager loading può semplificare il codice e migliorare le prestazioni delle nostre applicazioni, ma può anche portare a problemi di prestazioni se l’entità ha molte relazioni o se le relazioni hanno molte righe. L’utilizzo del lazy loading può migliorare le prestazioni delle nostre applicazioni riducendo il tempo di caricamento dei dati e il consumo di memoria, ma può anche causare eccezioni LazyInitializationException se non viene gestito correttamente. È importante comprendere le differenze tra eager e lazy loading e scegliere la soluzione migliore per le nostre esigenze specifiche.


Domande frequenti

Qual è la differenza tra eager e lazy loading in Hibernate?

La differenza principale tra eager e lazy loading in Hibernate è il momento in cui i dati associati vengono caricati. L’eager loading si riferisce al caricamento di tutti i dati associati di un’entità al momento della sua lettura, mentre il lazy loading si riferisce al caricamento dei dati associati solo quando sono richiesti. L’utilizzo di eager loading può semplificare il codice e migliorare le prestazioni delle nostre applicazioni, ma può anche portare a problemi di prestazioni se l’entità ha molte relazioni o se le relazioni hanno molte righe. L’utilizzo del lazy loading può migliorare le prestazioni delle nostre applicazioni riducendo il tempo di caricamento dei dati e il consumo di memoria, ma può anche causare eccezioni LazyInitializationException se non viene gestito correttamente.

Come posso evitare le eccezioni LazyInitializationException quando utilizzo il lazy loading in Hibernate?

Per evitare le eccezioni LazyInitializationException quando si utilizza il lazy loading in Hibernate, è possibile utilizzare la sessione di Hibernate per caricare esplicitamente i dati associati prima di chiudere la transazione. Ad esempio, se si utilizza il lazy loading per caricare una lista di progetti associati a un’entità Employee, è possibile utilizzare il metodo Hibernate.initialize per caricare esplicitamente i progetti prima di chiudere la transazione. In questo modo si garantisce che i dati associati siano stati caricati correttamente e che non verrà lanciata alcuna eccezione LazyInitializationException. È importante essere consapevoli delle possibili eccezioni che possono verificarsi quando si utilizza il lazy loading e di gestirle correttamente per garantire il corretto funzionamento delle nostre applicazioni.

Quando dovrei utilizzare l’eager loading e quando il lazy loading in Hibernate?

La scelta tra eager e lazy loading dipende dalle esigenze specifiche della tua applicazione. Se l’entità ha poche relazioni o se le relazioni hanno poche righe, l’eager loading può semplificare il codice e migliorare le prestazioni delle nostre applicazioni. Tuttavia, se l’entità ha molte relazioni o se le relazioni hanno molte righe, l’utilizzo di eager loading può portare a problemi di prestazioni. In questi casi, il lazy loading può migliorare le prestazioni delle nostre applicazioni riducendo il tempo di caricamento dei dati e il consumo di memoria. Tuttavia, è importante essere consapevoli delle possibili eccezioni che possono verificarsi quando si utilizza il lazy loading e di gestirle correttamente per garantire il corretto funzionamento delle nostre applicazioni. In ogni caso, la scelta tra eager e lazy loading dipende dalle esigenze specifiche della tua applicazione e dovrebbe essere basata su una valutazione attenta delle prestazioni e della complessità del codice.

Gestire banca dati MySQL con hibernate
Antonio Lamorgese


Desideri acquisire nuove competenze?

corsi.it

Seguire questo corso online ti offrirà
l'opportunità di acquisire nuove competenze e di
migliorare il tuo curriculum professionale.
Clicca qui per seguire le prime lezioni gratuite online

Corsi.it - Il portale Nr.1 al mondo di corsi online


Amministratore di rete e sviluppatore. Dopo anni di esperienza nel settore, ho ideato un sistema di gestione dati MYSQL in PHP senza scrivere una sola riga di codice. Scopri di più....