Tag: java, jpa, hibernate, orm, persistence, sql
Livello: intermedio
TL;DR: Standard JPA e implementazione Hibernate per la gestione del mapping relazionale, ciclo di vita delle entità e prevenzione del problema N+1.
JPA è la specifica standard Java EE/Jakarta per il mapping oggetto-relazionale.
Hibernate è il provider ORM più diffuso che implementa la specifica JPA.
EntityManager gestisce il Persistence Context e il ciclo di vita delle entità.
Stati dell'entità: Transient (nuova), Managed (nel contesto), Detached (scollegata), Removed (eliminata).
Il dirty checking rileva automaticamente i cambi di stato e li sincronizza a fine transazione.
FetchType.LAZY carica le relazioni on-demand; FetchType.EAGER le carica contestualmente.
L'annotazione @Transactional delimita il confine transazionale garantendo atomicità e rollback automatico in caso di errore.
La cache di primo livello è legata alla sessione/EntityManager corrente.
La cache di secondo livello è condivisa a livello di applicazione/SessionFactory.
Q: Come si gestisce o previene il problema delle N+1 query in JPA/Hibernate?R: Utilizzo query JPQL con JOIN FETCH esplicito o definisco un @EntityGraph per caricare le relazioni con una sola istruzione SQL.
Q: Qual è la differenza tra find() e getReference() in EntityManager?R: find() esegue subito la query sul database, mentre getReference() restituisce un proxy lazy senza interrogare il DB finché non si accede alle proprietà.
Q: Perché si verifica la LazyInitializationException?R: Si verifica quando provo ad accedere a una collezione o relazione con fetch lazy al di fuori di una sessione o transazione attiva.
Default di fetch non uniforme: le associazioni a singolo oggetto (@ManyToOne, @OneToOne) hanno default EAGER, mentre collezioni (@OneToMany) sono LAZY.
Risolvere LazyInitializationException usando EAGER su tutto il modello provoca gravi cali di prestazioni.
Modifica delle collezioni lazy dopo la chiusura del Persistence Context.
Mancato override coerente di equals() e hashCode() su entità gestite in Set.
Usare cascade = CascadeType.REMOVE su relazioni @ManyToMany eliminando record correlati non desiderati.
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String username;
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<Order> orders = new ArrayList<>();
}
Java
// Esempio risoluzione N+1 con JOIN FETCH
@Query("SELECT u FROM User u JOIN FETCH u.orders WHERE u.id = :id")
Optional<User> findByIdWithOrders(@Param("id") Long id);
[ ] Configurazione corretta della cache di secondo livello con Redis o Ehcache
[ ] Utilizzo di @BatchSize per ottimizzare le query lazy
[ ] Differenza operativa tra persist() e merge()
[x] Mappatura di base delle associazioni @ManyToOne e @OneToMany