đ§Ź Hibernate â ORM avancĂ©, Fetch, Cache, Batch, Logs & Tuning
Guide IDEO-Lab : Hibernate âproviderâ JPA orientĂ© production (N+1, graphs, batch, cache L2, logs SQL, statistiques, tuning).
Hibernate vs JPA
SpĂ©cification vs implĂ©mentation, ce quâHibernate ajoute.
JPAProviderExtensionsSession / Persistence Context
Identity map, dirty checking, flush modes, clear/detach.
SessionFlushDirtyMapping avancé Hibernate
Types, @Type, JSON, embeddables, converters, inherited mapping.
@TypeCustom typesInheritanceFetch strategies
LAZY/EAGER, join fetch, batch fetch, subselect, entity graphs.
FetchN+1GraphsN+1 : diagnostic & remédiation
SymptĂŽmes, outils, patterns robustes (DTO, ids page â fetch).
N+1SQL logsPlanBatching & bulk
Batch inserts/updates, ordering, flush/clear, bulk JPQL & stale context.
BatchFlush/ClearBulkCache L2 & query cache
Regions, invalidation, JCache, Ehcache, coherence trade-offs.
L2JCacheRegionsLocks & Concurrency
@Version, optimistic/pessimistic, timeouts, deadlocks, retries.
OptimisticPessimisticRetriesSQL généré & logs
Afficher SQL + bind params, slow queries, explain workflow.
SQLBindExplainStatistics & profiling
Hibernate Statistics, metrics par endpoint, query count, hotspots.
StatsMetricsHotspotsSchema & migrations
ddl-auto, naming, Flyway/Liquibase, production discipline.
DDLFlywayNamingCheat-sheet Hibernate
Rappels express : fetch, batching, cache, logs, tuning, piĂšges.
cheatbest practicesJPA = spec, Hibernate = moteur
JPA dĂ©finit des annotations + lâAPI (EntityManager). Hibernate est une implĂ©mentation (provider) qui ajoute des options de tuning, caches, statistics, types, fetch controls plus riches.
Where Hibernate matters most
- N+1 (fetch tuning)
- Batching writes
- Second-level cache & regions
- Custom types (JSON, enums, arrays)
- Statistics for profiling
Extensions utiles (exemples)
| Feature | Usage | Value |
|---|---|---|
| Batch fetching | charger plusieurs collections lazy en IN | réduit N+1 |
| Statistics | compteurs queries / hits cache | profiling |
| Custom types | JSON, arrays, money | mapping clean |
| Second-level cache | read-heavy | latence â |
RĂšgles de production (simple et strict)
- Disable Open Session In View (sinon lazy partout).
- Mesurer âqueries per requestâ (dĂ©tecter N+1).
- Batch + flush/clear en traitement massif.
- Cache L2 uniquement si invalidation maßtrisée.
- Migrations via Flyway/Liquibase (ddl-auto validate/none).
# English-only sample spring.jpa.open-in-view=false spring.jpa.hibernate.ddl-auto=validate
Identity map (L1) : mĂȘme ID â mĂȘme instance
Dans une session, Hibernate garantit lâidentitĂ©. Câest utile (cohĂ©rence) mais ça peut gonfler en mĂ©moire sur des traitements bulk.
// English-only conceptual states
transient -> persistent(managed) -> detached
| remove()
v
removedFlush = pousser SQL vers la DB
Flush ne commit pas forcément. Il envoie les INSERT/UPDATE/DELETE.
// English-only note FlushMode.AUTO: flush before query if needed FlushMode.COMMIT: flush only on commit FlushMode.MANUAL: explicit flush() only
clear() / detach() : contrÎler la mémoire
// English-only sample
for (int i = 0; i < items.size(); i++) {
session.persist(items.get(i));
if (i % 100 == 0) {
session.flush();
session.clear();
}
}La paire flush+clear est ton outil principal en bulk writes.
Pourquoi des types custom ?
- Garder un modĂšle mĂ©tier propre (Money, Email, JsonBlobâŠ).
- Eviter des hacks de colonnes et conversions dispersées.
- Centraliser validation + sérialisation.
// English-only sample (JPA converter pattern) @Converter public class MoneyConverter implements AttributeConverter{ public Long convertToDatabaseColumn(Money v) { return v == null ? null : v.cents(); } public Money convertToEntityAttribute(Long v) { return v == null ? null : Money.ofCents(v); } }
Precision & numeric pitfalls
- Monnaie : BigDecimal ou cents (Long) selon le besoin.
- Fixer precision/scale au niveau DB et mapping.
- Enums : toujours string pour stabilité.
// English-only sample @Enumerated(EnumType.STRING) @Column(nullable=false, length=32) private Status status;
JSON : le vrai choix = usage + index
| Approach | Good for | Trade-off |
|---|---|---|
| JSON column | flex fields | needs JSON indexes for search |
| Normalized tables | query-heavy | more joins |
| Serialized blob | write-heavy | no query on fields |
Fetch : le sujet #1 Hibernate en prod
- EAGER par défaut sur certains mappings = surprises.
- LAZY partout, puis charger explicitement ce quâil faut.
- DTO projections = stable pour API.
JOIN FETCH (ciblé)
// English-only sample Listorders = em.createQuery( "select distinct o from Order o " + "left join fetch o.lines " + "where o.status = :s", Order.class) .setParameter("s", "OPEN") .getResultList();
Prudence : join fetch dâune collection + pagination = souvent problĂ©matique.
Batch fetching
# English-only sample hibernate.default_batch_fetch_size=50
Quand une collection lazy est demandée, Hibernate charge plusieurs parents via IN.
EntityGraph (fetch plan explicite)
// English-only sample EntityGraphgraph = em.createEntityGraph(Order.class); graph.addAttributeNodes("customer"); Subgraph sg = graph.addSubgraph("lines"); sg.addAttributeNodes("product");
SymptĂŽmes typiques
- Latence qui explose avec le nombre dâitems.
- Logs SQL âmitrailletteâ (des centaines de SELECT).
- Pool DB saturé (threads en attente de connexion).
// English-only minimal N+1 example Listorders = em.createQuery("select o from Order o", Order.class).getResultList(); for (Order o : orders) { o.getLines().size(); }
Outils de diag (indispensables)
- Logs SQL + bind params (temporaire, en debug).
- Compteur de requĂȘtes par requĂȘte HTTP (AOP / metrics).
- Slow query log DB + EXPLAIN plans.
# English-only sample logging.level.org.hibernate.SQL=DEBUG logging.level.org.hibernate.orm.jdbc.bind=TRACE
Patterns qui tiennent la route
| Pattern | Quand | Impact |
|---|---|---|
| DTO projection | API/reads | data minimal + stable |
| JOIN FETCH | petit graphe | queries â |
| EntityGraph | fetch plan | pilotage propre |
| IDs page â fetch | pagination | Ă©vite duplicates/cartesian |
| Batch fetch size | lazy collections | IN queries |
Config batching (Hibernate)
# English-only sample hibernate.jdbc.batch_size=50 hibernate.order_inserts=true hibernate.order_updates=true hibernate.jdbc.batch_versioned_data=true
Ces options améliorent fortement le throughput en insert/update.
Pattern flush/clear
// English-only sample
for (int i = 0; i < items.size(); i++) {
session.persist(items.get(i));
if (i % 100 == 0) {
session.flush();
session.clear();
}
}Bulk JPQL : attention au contexte
Les bulk updates bypass le persistence context : les entitĂ©s dĂ©jĂ chargĂ©es deviennent âstaleâ.
// English-only sample
int n = em.createQuery("update Customer c set c.status = :s where c.lastLogin < :d")
.setParameter("s", "INACTIVE")
.setParameter("d", cutoff)
.executeUpdate();
em.clear();Quand L2 cache vaut le coup
- Read-heavy, reference data, faible taux de modification.
- Hot entities consultées trÚs souvent.
- Latence DB non négligeable.
Config (ex JCache)
# English-only sample hibernate.cache.use_second_level_cache=true hibernate.cache.region.factory_class=org.hibernate.cache.jcache.JCacheRegionFactory hibernate.javax.cache.provider=org.ehcache.jsr107.EhcacheCachingProvider
La configuration rĂ©elle dĂ©pend du provider cache (Ehcache/InfinispanâŠ).
Invalidation : le point critique
| Approach | Pro | Con |
|---|---|---|
| Read-only regions | fast | must truly be immutable |
| Read-write regions | safe | more overhead |
| No cache | simple | DB load |
Optimistic locking (scalable)
// English-only sample @Version private long version;
Excellent en faible contention : collisions gérées par retry.
Pessimistic lock
// English-only sample Account a = em.find(Account.class, id, LockModeType.PESSIMISTIC_WRITE);
à réserver aux hot rows critiques, TX courte, timeouts stricts.
Retry policy (must-have)
// English-only pseudo
for (int attempt = 1; attempt <= 3; attempt++) {
try { doWork(); break; }
catch (OptimisticLockException e) { sleep(backoff(attempt)); }
}Afficher SQL (temporaire)
# English-only sample logging.level.org.hibernate.SQL=DEBUG
En prod, préférer sampling + slow query log DB.
Voir les bind parameters
# English-only sample logging.level.org.hibernate.orm.jdbc.bind=TRACE
Workflow âDBA-likeâ
- Capturer la requĂȘte rĂ©elle (avec binds ou valeurs).
- Rejouer en SQL client.
- EXPLAIN ANALYZE (ou plan équivalent).
- Corriger : index / rewrite / fetch plan / pagination.
- Re-mesurer (p95/p99 + query count).
Enable statistics
# English-only sample hibernate.generate_statistics=true
Utilise en staging, ou ponctuellement en prod (avec prudence) pour diagnostiquer.
Ce que tu veux voir
- Query count / entity load count.
- L2 cache hit/miss.
- Collection fetch count (detect N+1).
KPIs par endpoint
- p95/p99 latency
- db time vs app time
- queries/request
- pool wait time
// English-only concept request_id -> controller -> service -> repo metrics: queries, db_ms, total_ms
Discipline production
- ddl-auto : validate/none
- migrations versionnées (Flyway/Liquibase)
- review + tests sur dataset réaliste
# English-only sample spring.jpa.hibernate.ddl-auto=validate
Naming & index strategy
- Fixer naming strategy pour éviter drift.
- Index design cĂŽtĂ© migrations, pas via annotations âau petit bonheurâ.
- Sur DB : monitor index bloat / unused indexes.
Fetch / N+1
// English-only quick notes Disable open-in-view Prefer DTO projections for API Use join fetch for small graphs Use EntityGraph for explicit fetch plan For pagination: page IDs then fetch graph Enable batch fetch size to reduce N+1
Batching
// English-only quick notes hibernate.jdbc.batch_size=50 hibernate.order_inserts=true hibernate.order_updates=true flush+clear every N Bulk updates -> clear() after
Cache / Logs / Stats
// English-only quick notes Use L2 cache only with clear invalidation rules Track cache hit/miss SQL logs + bind only for debug (sensitive) Use slow query log + explain plans Enable generate_statistics for profiling
Concurrency
// English-only quick notes Use @Version for optimistic locking Implement retry with backoff Use pessimistic locks only when needed Keep transactions short
