Chaturmind
LearnDSASystem DesignInterview PrepDevOpsEngineering GrowthBlog
Start learning
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML
  • DevOps
  • Engineering Growth
  • Java Interview Prep

Company

  • Blog
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Java Interview Prep: 8+ Years (Senior & Lead)

Expert Core Java

  • Tricky Java Output, Operators & OOP Edge Cases — Interview Questions
  • Tricky Exceptions, Memory & Keyword Questions — Interview Questions
  • Classic Java Language Questions, Senior-Grade Answers — Interview Questions
  • Classic Collections, Threads & JDK APIs, Senior-Grade Answers — Interview Questions
  • Reflection, Dynamic Proxies, final & Modern OOP Design — Interview Questions

JVM Internals & Performance

  • Class Loading, Bytecode & Object Layout — Interview Questions
  • JIT Compilation & Runtime Optimisations — Interview Questions
  • Garbage Collectors Deep Dive — Interview Questions
  • JVM Tuning, GC Logs & Memory Footprint — Interview Questions
  • Memory Leaks, OutOfMemoryErrors & Profiling Tools — Interview Questions
  • Modules, Agents & Advanced JVM APIs — Interview Questions

Collections & Concurrency at Scale

  • Collections Internals & Complexity — Interview Questions
  • Iterators, Comparators & Ordering Contracts — Interview Questions
  • Concurrent Collections, Queues & Lock-Free Structures — Interview Questions
  • Threads, Executors & ForkJoin Internals — Interview Questions
  • Locks, Atomics, CAS & Synchronizers — Interview Questions
  • Java Memory Model, volatile, Fences & ThreadLocal — Interview Questions
  • Deadlock, Livelock, Starvation & Concurrent Design — Interview Questions
  • CompletableFuture, Parallel Streams & Non-Blocking I/O — Interview Questions

Modern Java (8 to 21+)

  • Lambdas & Functional Interfaces Internals — Interview Questions
  • Streams & Collectors Deep Dive — Interview Questions
  • Optional & Interface Default/Static Methods — Interview Questions
  • Java 9–25 Features & Virtual Threads — Interview Questions

Design Patterns, SOLID & Clean Code

  • Design Pattern Trade-offs & Combinations — Interview Questions
  • SOLID, Clean Code & Anti-Patterns — Interview Questions

Spring & Spring Boot Internals

  • IoC, Dependency Injection & Bean Lifecycle Internals — Interview Questions
  • Spring AOP, Proxies & @Async Internals — Interview Questions
  • Spring Configuration, Auto-Configuration & Custom Starters — Interview Questions
  • Spring MVC & REST Internals, Exception Frameworks — Interview Questions
  • Spring Security Advanced Internals — Interview Questions
  • Spring WebFlux, Reactor & R2DBC — Interview Questions
  • Spring Cloud, Observability & Distributed Tracing — Interview Questions
  • Spring Boot 3, Native Images & Production Scenarios — Interview Questions

JPA, Hibernate & Databases at Scale

  • Spring Data JPA — Queries, Projections, Custom Repositories & Locking — Interview Questions
  • JPA Entity Mapping, Associations & Cascades — Interview Questions
  • JPQL vs Native Queries in Depth — Interview Questions
  • Hibernate Caching — First-Level, Second-Level & Query Cache — Interview Questions
  • Lazy vs Eager Loading, LazyInitializationException & N+1 — Interview Questions
  • JPA Transactions, Propagation, Isolation & Dirty Checking — Interview Questions
  • SQL vs NoSQL, Indexing & Query Tuning — Interview Questions
  • Database Scaling, Replication, Pooling & Consistency Models — Interview Questions
  • Redis, Search, Time-Series, CDC & Transactional Data Modelling — Interview Questions

Testing Strategy & API Design

  • Spring Boot Test Slices, Context & Test Strategy — Interview Questions
  • Testing Web, Persistence, Security, Async & Messaging in Spring Boot — Interview Questions
  • JUnit 5 & Mockito, Advanced — Interview Questions
Chaturmind
← Java Interview Prep: 8+ Years (Senior & Lead)

Expert Core Java

  • Tricky Java Output, Operators & OOP Edge Cases — Interview Questions
  • Tricky Exceptions, Memory & Keyword Questions — Interview Questions
  • Classic Java Language Questions, Senior-Grade Answers — Interview Questions
  • Classic Collections, Threads & JDK APIs, Senior-Grade Answers — Interview Questions
  • Reflection, Dynamic Proxies, final & Modern OOP Design — Interview Questions

JVM Internals & Performance

  • Class Loading, Bytecode & Object Layout — Interview Questions
  • JIT Compilation & Runtime Optimisations — Interview Questions
  • Garbage Collectors Deep Dive — Interview Questions
  • JVM Tuning, GC Logs & Memory Footprint — Interview Questions
  • Memory Leaks, OutOfMemoryErrors & Profiling Tools — Interview Questions
  • Modules, Agents & Advanced JVM APIs — Interview Questions

Collections & Concurrency at Scale

  • Collections Internals & Complexity — Interview Questions
  • Iterators, Comparators & Ordering Contracts — Interview Questions
  • Concurrent Collections, Queues & Lock-Free Structures — Interview Questions
  • Threads, Executors & ForkJoin Internals — Interview Questions
  • Locks, Atomics, CAS & Synchronizers — Interview Questions
  • Java Memory Model, volatile, Fences & ThreadLocal — Interview Questions
  • Deadlock, Livelock, Starvation & Concurrent Design — Interview Questions
  • CompletableFuture, Parallel Streams & Non-Blocking I/O — Interview Questions

Modern Java (8 to 21+)

  • Lambdas & Functional Interfaces Internals — Interview Questions
  • Streams & Collectors Deep Dive — Interview Questions
  • Optional & Interface Default/Static Methods — Interview Questions
  • Java 9–25 Features & Virtual Threads — Interview Questions

Design Patterns, SOLID & Clean Code

  • Design Pattern Trade-offs & Combinations — Interview Questions
  • SOLID, Clean Code & Anti-Patterns — Interview Questions

Spring & Spring Boot Internals

  • IoC, Dependency Injection & Bean Lifecycle Internals — Interview Questions
  • Spring AOP, Proxies & @Async Internals — Interview Questions
  • Spring Configuration, Auto-Configuration & Custom Starters — Interview Questions
  • Spring MVC & REST Internals, Exception Frameworks — Interview Questions
  • Spring Security Advanced Internals — Interview Questions
  • Spring WebFlux, Reactor & R2DBC — Interview Questions
  • Spring Cloud, Observability & Distributed Tracing — Interview Questions
  • Spring Boot 3, Native Images & Production Scenarios — Interview Questions

JPA, Hibernate & Databases at Scale

  • Spring Data JPA — Queries, Projections, Custom Repositories & Locking — Interview Questions
  • JPA Entity Mapping, Associations & Cascades — Interview Questions
  • JPQL vs Native Queries in Depth — Interview Questions
  • Hibernate Caching — First-Level, Second-Level & Query Cache — Interview Questions
  • Lazy vs Eager Loading, LazyInitializationException & N+1 — Interview Questions
  • JPA Transactions, Propagation, Isolation & Dirty Checking — Interview Questions
  • SQL vs NoSQL, Indexing & Query Tuning — Interview Questions
  • Database Scaling, Replication, Pooling & Consistency Models — Interview Questions
  • Redis, Search, Time-Series, CDC & Transactional Data Modelling — Interview Questions

Testing Strategy & API Design

  • Spring Boot Test Slices, Context & Test Strategy — Interview Questions
  • Testing Web, Persistence, Security, Async & Messaging in Spring Boot — Interview Questions
  • JUnit 5 & Mockito, Advanced — Interview Questions
HomeLearnJava Interview PrepJava Interview Prep: 8+ Years (Senior & Lead)JPA, Hibernate & Databases at Scale
✓ FreeAdvanced· 9 min read

Lazy vs Eager Loading, LazyInitializationException & N+1 — Interview Questions

When lazy beats eager, how Hibernate handles lazy loading outside transactions (and why open-session-in-view is a trap), accessing lazy fields after the session closes, pitfalls of EAGER in large graphs and Cartesian products, bytecode enhancement and lazy basic attributes, forcing initialization, how fetch types and fetch joins affect performance, detecting and fixing N+1, lazy loading in DTO projections, and lazy loading's impact on REST APIs.

Published September 25, 2026


How to use this lesson

Default everything to LAZY, and fetch per use case. That's the senior rule. The questions test whether you know why eager defaults cause N+1 queries and huge graphs, how proxies work, and the legitimate fixes: fetch joins, entity graphs, batch fetching, and DTO projections.

Q1. When should you prefer lazy loading over eager loading?

Short answer: Almost always, as the mapping default. Lazy loading defers loading an association until it's accessed, so each use case can decide what to fetch. Eager mappings load the association every time the entity is loaded, by any query, including in lists, where it multiplies into N+1 selects or huge joins. Eager fetching is appropriate only per query (fetch joins or entity graphs) when you know the association is needed. At the mapping level, it's rarely right: perhaps for a tiny, always-needed @ManyToOne to a cached reference entity.

Learn it in depth → The N+1 Query Problem

Q2. How does Hibernate handle lazy loading outside a transaction? What happens if you access a lazy field after the session is closed?

Short answer: Lazy associations are proxies (for @ManyToOne/@OneToOne) or persistent collections (PersistentBag/PersistentSet) that need an open session to load their data. Accessing one after the session is closed (outside @Transactional, in the controller, or during JSON serialisation) throws LazyInitializationException: could not initialize proxy – no Session.

The workarounds, and their costs:

  • Open Session in View (OSIV) keeps the session open for the whole web request. Spring Boot enables it by default (spring.jpa.open-in-view=true, and logs a warning). It hides the problem, but it causes lazy loading during view or JSON rendering: unpredictable N+1 queries, database connections held for the whole request, and SQL issued from the presentation layer. Disable it (spring.jpa.open-in-view=false), and fetch explicitly.
  • hibernate.enable_lazy_load_no_trans=true opens a temporary session per lazy load. It's an anti-pattern: hidden N+1 queries, no transactional consistency.

Q3. What are the alternatives for handling lazy loading outside the session?

Short answer: Load what you need inside the transaction, in the service layer:

  1. Fetch joins: select o from Order o join fetch o.lines where o.id = :id.
  2. @EntityGraph on repository methods.
  3. DTO projections: fetch exactly the columns needed, with no proxies at all (the best option for APIs).
  4. Batch fetching (@BatchSize(size = 50), or hibernate.default_batch_fetch_size=50), which loads the lazy associations of many parents in IN (...) batches.
  5. Explicit initialisation in the transaction: Hibernate.initialize(order.getLines()).
  6. Map to DTOs inside the transaction, then return the DTOs.

Q4. What are the common pitfalls of EAGER fetching, especially in large object graphs?

Short answer:

  • Always loaded: every query for the entity also loads the eager associations, even when they aren't needed, so you over-fetch memory and I/O.
  • N+1 queries: with JPQL queries (which ignore the mapping's fetch type for the initial SQL), Hibernate issues a separate SELECT per row for each eager association, after the main query.
  • Cascading eagerness: eager associations of eager associations load huge graphs.
  • Cartesian products when several eager collections are joined.
  • It can't be turned off per query: you can make a lazy association eager in a query, but not the reverse.
  • It causes surprising performance cliffs as data grows.

The fix: LAZY everywhere, plus explicit fetching per use case.

Q5. Can eager loading lead to Cartesian product problems?

Short answer: Yes. Fetching two or more collections in one SQL query (through eager mappings or fetch joins) joins them all. An order with 10 lines and 5 payments returns 50 rows, duplicating the data. With more collections and more rows, the result grows multiplicatively, which is slow, and memory-hungry. Hibernate even refuses two bags (MultipleBagFetchException).

Fixes:

  • fetch one collection per query, with a join, and load the others with separate queries or batch fetching (Hibernate reuses the persistence context, so the entities merge);
  • use Sets (which avoid the exception, but not the row explosion);
  • use FetchMode.SUBSELECT;
  • use DTO projections for read models.

Q6. What is bytecode enhancement, and how does it help lazy loading? Can basic attributes be lazy?

Short answer: Hibernate bytecode enhancement (through the Maven or Gradle plugin at build time, or a runtime agent) instruments entity classes, adding interception to field access. It enables:

  • lazy loading of basic attributes: @Basic(fetch = LAZY) on large columns (@Lob documents, JSON blobs), which otherwise can't be lazy. The hint is ignored without enhancement. Grouped with @LazyGroup;
  • true lazy @OneToOne on the inverse side (without enhancement, the inverse one-to-one must be loaded eagerly, to know whether it's null);
  • dirty tracking without snapshot comparison (faster flushes);
  • association management (bidirectional synchronisation).

The trade-offs: build complexity, and debugging surprises. Many teams instead move large columns to a separate entity, or table, fetched on demand.

Q7. How do you force initialisation of a lazy collection?

Short answer: Inside an open session or transaction:

  • Hibernate.initialize(order.getLines()) (or Hibernate.initialize(proxy));
  • touching the collection (order.getLines().size()) works, but reads as a hack;
  • better: fetch it in the query (join fetch, or an @EntityGraph), so it's loaded in the same SQL, instead of a second round trip.

Check the state with Hibernate.isInitialized(...). After the session has closed, you'd need to reattach (merge) the entity, or re-query.

Q8. How does the fetch type affect performance? How do fetch joins affect lazy associations?

Short answer:

  • The fetch type decides when associations are loaded. The fetch strategy decides how: join, separate select, batch or subselect.
    • EAGER risks over-fetching, N+1, and Cartesian products.
    • LAZY risks N+1 when you iterate over collections of parents without planning.
  • Fetch joins (join fetch) override LAZY for that query: the association is loaded in the same SQL, and initialised in the persistence context, so the proxy becomes a real object. The caveats:
    • one collection per query (because of the Cartesian product);
    • pagination with collection fetch joins happens in memory (HHH90003004: firstResult/maxResults specified with collection fetch; applying in memory), so for paging, fetch IDs first, then fetch the entities by those IDs;
    • you can't use an alias in a WHERE on the fetched collection without filtering the collection itself (that's semantically risky).

Q9. How do you detect and fix the N+1 problem caused by lazy loading?

Short answer:

  • Detect it:
    • SQL logs in development: one query, then N identical queries with different IDs;
    • Hibernate statistics (the query count per request);
    • datasource-proxy or p6spy, with query-count assertions in tests (assertSelectCount(2));
    • APM traces showing many small database spans;
    • Hypersistence Optimizer.
  • Fix it:
    • join fetch or @EntityGraph for the use case;
    • batch fetching (default_batch_fetch_size), which turns N+1 into 1 + N/50;
    • DTO projections with explicit joins;
    • @ManyToOne(fetch = LAZY) everywhere, to avoid eager-induced N+1.
  • Prevent it: disable OSIV, so lazy loads can't happen in the view layer, and add query-count tests for critical endpoints.
@Query("select distinct o from Order o join fetch o.customer left join fetch o.lines where o.status = :status")
List<Order> findWithCustomerAndLines(@Param("status") OrderStatus status);   // 1 query instead of 1 + 2N

Q10. How does JPA handle lazy loading in DTO projections?

Short answer: DTO projections don't involve lazy loading at all. Constructor expressions, record projections and closed interface projections select exactly the listed columns through joins in the query, and produce plain objects, with no proxies or persistence context. So there's no LazyInitializationException, and no N+1, as long as the projection doesn't contain entity references. The caveats:

  • if a DTO constructor takes an entity argument (new Dto(o.customer)), that entity is managed and its lazy fields can still fail. Project scalar paths (o.customer.name) instead;
  • open interface projections (SpEL @Value("#{target...}")) load the full entity, so lazy access in the expression can trigger queries.

Q11. What's the impact of lazy loading on REST APIs?

Short answer: Returning entities from controllers creates several problems:

  • Serialisation triggers lazy loads: with OSIV on, you get N+1 queries during JSON rendering, and with OSIV off, you get LazyInitializationException.
  • Hibernate proxies serialise badly (hibernateLazyInitializer fields, needing jackson-datatype-hibernate), and infinite recursion on bidirectional links.
  • The API shape is coupled to the schema, you over-expose fields, and there's a mass-assignment risk on input.

The practice: API DTOs assembled in the service layer, inside a transaction, from explicitly fetched data (projections or fetch joins). Disable OSIV, and document and test the query counts per endpoint.

Q12. What's the default fetch type for each association? (The short version)

Short answer: @ManyToOne and @OneToOne are EAGER. @OneToMany and @ManyToMany are LAZY. Basic attributes are eager (lazy needs enhancement). Override all the to-one associations to LAZY.

Follow-up questions this topic invites — and their answers

Q: Why does distinct appear in fetch-join queries? A: A collection fetch join returns one row per child, so the parent appears several times in the result. select distinct deduplicates the parent entities. In Hibernate 6, this deduplication happens automatically in memory, and doesn't need to be passed to SQL.

Q: What is @Fetch(FetchMode.SUBSELECT)? A: When one parent's collection is initialised, Hibernate loads the collections of all the parents from the original query, with one subselect (where parent_id in (select id from … original query …)). That's two queries in total.

Q: How do you paginate parents while also fetching their children efficiently? A: Page over the parent IDs (a query with limit/offset, or keyset), then fetch those parents with join fetch on the collection, where p.id in :ids. Or use batch fetching for the children.

Q: Is FetchType.LAZY on @ManyToOne always honoured? A: Yes, with proxies, as long as the target class isn't final and the ID is available. For the inverse side of @OneToOne, laziness needs bytecode enhancement, or @MapsId.

Previous

Hibernate Caching — First-Level, Second-Level & Query Cache — Interview Questions

Next

JPA Transactions, Propagation, Isolation & Dirty Checking — Interview Questions

AI Tutor

Lesson: Lazy vs Eager Loading, LazyInitializationException & N+1 — Interview Questions

Quick actions

AI responses can be inaccurate. Verify critical information.