Hibernate Lazy Loading: FetchType LAZY vs EAGER with SQL

Hibernate lazy loading reads an association only when the code first uses it. See the SQL that LAZY and EAGER run, how proxies and PersistentBag work, and how to fix LazyInitializationException with join fetch, entity graphs, Hibernate.initialize() or a DTO projection.

Lazy loading means that Hibernate reads an associated entity or collection from the database only when our code first uses it, not when it loads the entity that owns the association. Until that first use, the field holds a placeholder, which is a proxy for a single entity or an uninitialized collection for a list.

We use lazy loading to keep each query small, so a screen that lists menu items does not also read every restaurant and every menu behind them.

The following example maps the two associations between a Restaurant and its MenuItem entities, where each MenuItem belongs to one Restaurant. We map the MenuItem.restaurant field as @ManyToOne(fetch = FetchType.LAZY), and the Restaurant.menu collection is a @OneToMany, which is LAZY by default.

// MenuItem
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "restaurant_id")
private Restaurant restaurant;

// Restaurant
@OneToMany(mappedBy = "restaurant", cascade = CascadeType.ALL)    // LAZY by default
private List<MenuItem> menu = new ArrayList<>();

We load one MenuItem with em.find() and watch the SQL that Hibernate logs at each step. Notice that em.find() reads only the MenuItem row, and getRestaurant() returns a proxy without any SQL. Hibernate runs the SELECT for the restaurant only when we call getName(), and the SELECT for the menu only when we call getMenu().size().

emf.runInTransaction(em -> {
  MenuItem pasta = em.find(MenuItem.class, pastaId);   // select ... from MenuItem mi1_0 where mi1_0.id=?
  Restaurant restaurant = pasta.getRestaurant();       // no SQL, a proxy
  String name = restaurant.getName();                  // select ... from Restaurant r1_0 where r1_0.id=?
  int menuSize = restaurant.getMenu().size();          // select ... from MenuItem m1_0 where m1_0.restaurant_id=?
});

Next, we look at the proxies and lazy collections that Hibernate puts into the fields, and at the fixes for LazyInitializationException and the N+1 problem.

1. How Lazy Loading Works in Hibernate

A food delivery app shows 50 dishes on its home screen. Each dish card needs the dish name and the price, nothing more. But each MenuItem entity also points to its Restaurant, and each restaurant has a menu of 40 more dishes, so loading all of that for every card would pull thousands of rows that nobody looks at. Lazy loading lets us load the 50 dishes first and read a restaurant only when the user taps a dish to see where it comes from.

Every association between two entities has a fetch type, which tells Hibernate when to read the associated rows from the database. Jakarta Persistence defines two values in the FetchType enum.

  • FetchType.LAZY tells Hibernate to read the association later, at the first access. The value is only a hint for the persistence provider (“Data may be lazily fetched”), so the provider is allowed to load the data earlier.
  • FetchType.EAGER tells Hibernate to read the association together with its owner, and this value is a requirement (“Data must be eagerly fetched”).

The same em.find() call runs different SQL depending on the fetch type of the association. In our example, MenuItem.restaurant is mapped as LAZY, whereas DailySpecial.item keeps the default EAGER, so the restaurant needs a second SELECT and the item comes with the first one.

Two timelines. MenuItem.restaurant with fetch LAZY: em.find runs a select from MenuItem only, getRestaurant and getId run no SQL, getName runs a select from Restaurant. DailySpecial.item with the default EAGER: em.find runs one select from DailySpecial left join MenuItem, and reading the item later runs no SQL
LAZY splits the load into two small SELECT statements and skips the second one when the code never reads the restaurant, whereas EAGER adds the join to every load.

Apart from the SQL, the two fetch types also differ in what the field holds before the first access and in what happens after the transaction ends.

LAZYEAGER
SQL when Hibernate loads the ownerOnly the owner’s tableThe owner’s table joined with the association’s table
Value of the field before the first accessA proxy or an uninitialized collectionThe real entity or an initialized collection
Reading the field after the transactionThrows LazyInitializationExceptionWorks
Loading 3 owners with a JPQL query1 SELECT1 + 3 SELECT statements
Can a query change it?Yes, join fetch or an entity graph loads it in the same queryOnly a fetchgraph hint can skip it

An EAGER association is loaded every time Hibernate loads the entity, even when the code never uses it. The EAGER load adds a join or an extra query to every load, as the last two rows of the table show. A LAZY association is loaded only when we access it, or earlier when a query asks for it with join fetch or an entity graph. So we map every association as LAZY, and when a use case needs the related data, we fetch it in that query. Hibernate recommends the same practice in its user guide.

1.1. Default Fetch Types in JPA

When we leave out the fetch attribute, Jakarta Persistence takes the default fetch type from the association annotation. Associations that end in a collection are LAZY, and associations that end in a single entity are EAGER.

AnnotationDefault fetch typeWhat we set
@OneToManyLAZYKeep the default
@ManyToManyLAZYKeep the default
@ElementCollectionLAZYKeep the default
@ManyToOneEAGERfetch = FetchType.LAZY
@OneToOneEAGERfetch = FetchType.LAZY
@Basic (a simple column such as name)EAGERKeep the default, as FAQ 3.3 explains

We can see that only the to-one annotations need a change, so fetch = FetchType.LAZY on @ManyToOne and @OneToOne is the line we add most often.

1.2. Proxies and Lazy Collections

Hibernate needs an object to put into a field that it has not loaded yet, so it uses one of two kinds of placeholders, depending on the association type.

  • For a to-one association (@ManyToOne, @OneToOne), Hibernate creates a proxy, which is a subclass of the entity class that Hibernate generates at runtime and that holds only the id. For example, menuItem.getRestaurant() returns a Restaurant$HibernateProxy that knows the id 1 but not the restaurant name.
  • For a collection (@OneToMany, @ManyToMany), Hibernate puts its own collection class into the field, so a List becomes an org.hibernate.collection.spi.PersistentBag that knows the owner’s key but holds no elements yet.
Left: the Pasta menu item points to a Restaurant$HibernateProxy, a runtime subclass of Restaurant that holds id 1, has no name loaded and is linked to the open EntityManager; getId runs no SQL and getName runs a select. Right: the Green Bowl restaurant points to a PersistentBag with owner key 1 and no elements; getMenu runs no SQL and size, a loop or toString run one select, and the loaded elements are real MenuItem objects. Bottom: after the EntityManager is closed the first load throws LazyInitializationException; only getId on a proxy still works
Both placeholders load their data through the EntityManager that created them, so once that EntityManager is closed, only the proxy’s id is still available.

We can check whether a placeholder is loaded with Hibernate.isInitialized() or PersistenceUnitUtil.isLoaded(), and neither call runs SQL. The exception is Hibernate.getClass(), because it needs the real entity class and so initializes the proxy with one SELECT.

Restaurant proxy = menuItem.getRestaurant();
boolean menuLoaded = Hibernate.isInitialized(restaurant.getMenu());                    // false
boolean menuLoadedJpa = emf.getPersistenceUnitUtil().isLoaded(restaurant, "menu");    // false, the JPA way
boolean isRestaurant = proxy instanceof Restaurant;                                    // true
String className = proxy.getClass().getName();      // com.howtodoinjava.hibernate.lazy.Restaurant$HibernateProxy
Class<?> entityClass = Hibernate.getClass(proxy);   // Restaurant.class, but this loads the proxy (1 SELECT)

Notice that instanceof Restaurant returns true for the proxy, because the proxy class extends Restaurant. The elements inside an initialized PersistentBag are real MenuItem objects, not proxies.

1.3. When Does Hibernate Run the Second SELECT?

Calling the getter of a lazy field returns the placeholder without running any SQL. The SELECT runs only when we call a method that needs the data inside the placeholder.

CallSQL
menuItem.getRestaurant()None, it returns the proxy
proxy.getId()None, because the proxy already holds the id
proxy.getName() or any other getterselect … from Restaurant where id=?
Hibernate.getClass(proxy)select … from Restaurant where id=?
restaurant.getMenu()None, it returns the PersistentBag
menu.size(), menu.isEmpty(), menu.iterator()select … from MenuItem where restaurant_id=?
menu.toString() (also a log statement or the debugger)select … from MenuItem where restaurant_id=?

The toString() row matters in practice, because a log statement or a debugger watch on the collection initializes it and runs the same MenuItem query.

2. Hibernate Lazy Loading Example

The following example is a small restaurant directory with restaurants, their menu items and a daily special per weekday. It runs on Hibernate 7.4, Java 25 and an in-memory H2 database. The project on GitHub prints the SQL of every step and includes 27 JUnit tests.

2.1. Restaurant and Menu Item Mapping

A restaurant has many menu items and each menu item belongs to one restaurant, whereas a daily special points to one menu item. The sample data has three restaurants, and Green Bowl serves Pasta and Salad.

Restaurant   (id, name)                                     Green Bowl, Taco Town, Spice Hut
MenuItem     (id, name, price, description, restaurant_id)  Pasta 8.00, Salad 6.00, Tacos 5.00, ...
DailySpecial (id, weekday, item_id)                         Monday: Pasta, Tuesday: Tacos, ...

The collection side Restaurant.menu uses mappedBy, so the foreign key column restaurant_id belongs to the MenuItem table. Only DailySpecial.item keeps the EAGER default, so that we can compare both fetch types on the same data.

@OneToMany(mappedBy = "restaurant", cascade = CascadeType.ALL)    // LAZY by default
private List<MenuItem> menu = new ArrayList<>();

public void addItem(MenuItem item) {
  menu.add(item);
  item.setRestaurant(this);
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "restaurant_id")
private Restaurant restaurant;
@ManyToOne                        // EAGER by default
@JoinColumn(name = "item_id")
private MenuItem item;

We build the EntityManagerFactory with HibernatePersistenceConfiguration and turn on SQL logging and statistics, so we can see every statement that each step runs.

2.2. Loading a Restaurant and Its Menu

The call em.find(Restaurant.class, greenBowlId) reads only the Restaurant row, and getMenu() returns an uninitialized PersistentBag without running SQL. Notice that the MenuItem query runs only at menu.size(), which returns 2 for Green Bowl.

emf.runInTransaction(em -> {
  Restaurant restaurant = em.find(Restaurant.class, greenBowlId);
  // select r1_0.id,r1_0.name from Restaurant r1_0 where r1_0.id=?

  List<MenuItem> menu = restaurant.getMenu();         // no SQL
  boolean loaded = Hibernate.isInitialized(menu);     // false

  int size = menu.size();                             // 2
  // select m1_0.restaurant_id,m1_0.id,...,m1_0.price from MenuItem m1_0 where m1_0.restaurant_id=?
});

2.3. Loading a Menu Item and Its Restaurant

The call em.find(MenuItem.class, pastaId) reads the restaurant_id column together with the menu item row and stops there, because that value is enough to build the proxy. Notice that getId() returns 1 from the proxy without SQL, whereas getName() runs the select … from Restaurant statement.

emf.runInTransaction(em -> {
  MenuItem pasta = em.find(MenuItem.class, pastaId);
  // select mi1_0.id,mi1_0.description,mi1_0.name,mi1_0.price,mi1_0.restaurant_id from MenuItem mi1_0 where mi1_0.id=?

  Restaurant restaurant = pasta.getRestaurant();      // Restaurant$HibernateProxy, no SQL
  Long id = restaurant.getId();                       // 1, no SQL
  String name = restaurant.getName();                 // "Green Bowl"
  // select r1_0.id,r1_0.name from Restaurant r1_0 where r1_0.id=?
});

For comparison, Hibernate joins the EAGER association DailySpecial.item into the first query with a left join MenuItem, so the later getItem().getName() call runs no SQL.

DailySpecial monday = em.find(DailySpecial.class, mondayId);
// select ds1_0.id,i1_0.id,...,ds1_0.weekday from DailySpecial ds1_0 left join MenuItem i1_0 on i1_0.id=ds1_0.item_id where ds1_0.id=?
String itemName = monday.getItem().getName();         // "Pasta", no SQL

The method em.getReference() returns the same kind of proxy, which is the main difference between find() and getReference().

2.4. Reading a Lazy Association After the Transaction

Most of us first meet LazyInitializationException in a REST endpoint. The service method loads a Restaurant and returns it, the transaction ends, and Jackson reads the menu later while it writes the JSON response. With spring.jpa.open-in-view=false, Jackson’s read of the menu fails.

A proxy or a lazy collection can load its data only while the EntityManager (the Hibernate Session) that created it is open. After runInTransaction() or callInTransaction() returns, the EntityManager is closed and the entity becomes a detached entity. So the first access that needs SQL throws LazyInitializationException, whereas getMenu() itself does not throw, because it returns the placeholder without SQL.

Restaurant detached = emf.callInTransaction(em -> em.find(Restaurant.class, greenBowlId));
List<MenuItem> menu = detached.getMenu();      // no exception yet
int size = detached.getMenu().size();          // throws LazyInitializationException
org.hibernate.LazyInitializationException: Cannot lazily initialize collection of role 'com.howtodoinjava.hibernate.lazy.Restaurant.menu' with key '1' (no session)

A detached proxy behaves the same way, except that getId() still works, because the proxy holds the id and needs no SQL for it.

MenuItem detached = emf.callInTransaction(em -> em.find(MenuItem.class, pastaId));
Long id = detached.getRestaurant().getId();         // 1, works
String name = detached.getRestaurant().getName();   // throws LazyInitializationException
org.hibernate.LazyInitializationException: Could not initialize proxy [com.howtodoinjava.hibernate.lazy.Restaurant#1] - no session

The fix is to load the data before the transaction ends, in most cases in the query that loads the entity. Which fix fits depends on what the caller needs, such as plain values for a JSON response or managed entities for further changes.

Decision tree for code that reads a LAZY association after the transaction. If the caller needs only values for a screen or an API, use a DTO projection with a record result type, 1 SELECT. If it needs entities: when we write the JPQL query use join fetch, 1 SELECT; for find or a reused query use an entity graph, em.find with a graph or a fetchgraph hint, 1 SELECT; when the entity is already loaded call Hibernate.initialize before the commit, one extra SELECT per association. A red box lists what is not a fix: open session in view, hibernate.enable_lazy_load_no_trans and changing the mapping to FetchType.EAGER
We pick the fix by what the caller needs. Each green option loads the data inside the transaction, whereas the red options only hide the exception.

2.5. Fixing It With join fetch

A join fetch in a JPQL query loads the restaurant and its menu in one SELECT, so the collection is initialized before the transaction ends. We can see that Hibernate translates it into an SQL join MenuItem, and getMenu() returns [Pasta, Salad] after the commit.

Restaurant restaurant = emf.callInTransaction(em -> em.createQuery(
        "select r from Restaurant r join fetch r.menu where r.id = :id", Restaurant.class)
    .setParameter("id", greenBowlId)
    .getSingleResult());
// select r1_0.id,m1_0.restaurant_id,m1_0.id,...,r1_0.name from Restaurant r1_0
//   join MenuItem m1_0 on r1_0.id=m1_0.restaurant_id where r1_0.id=?

List<MenuItem> menu = restaurant.getMenu();   // [Pasta, Salad], after the commit

join fetch is an inner join. So a restaurant that has no menu items is missing from the result, and when we need those restaurants too, left join fetch returns them with an empty, initialized menu.

2.6. Fixing It With an Entity Graph

An entity graph lists the attributes to load separately from the query, so we can reuse one graph with find() and with queries that serve several screens. Jakarta Persistence 3.2 added em.find(EntityGraph, id) for this case. Notice that Hibernate turns the graph into a left join MenuItem, so a restaurant without menu items still comes back.

Restaurant restaurant = emf.callInTransaction(em -> {
  EntityGraph<Restaurant> graph = em.createEntityGraph(Restaurant.class);
  graph.addAttributeNode("menu");
  return em.find(graph, greenBowlId);
});
// select r1_0.id,r1_0.name,m1_0.restaurant_id,m1_0.id,... from Restaurant r1_0
//   left join MenuItem m1_0 on r1_0.id=m1_0.restaurant_id where r1_0.id=?

For a graph that several queries share, we declare a named entity graph once on the entity and pass it to each query as the jakarta.persistence.fetchgraph hint. With a fetch graph, Hibernate loads the attributes in the graph and treats every attribute outside the graph as LAZY.

@NamedEntityGraph(name = "Restaurant.menu", attributeNodes = @NamedAttributeNode("menu"))

Restaurant restaurant = em.createQuery("select r from Restaurant r where r.id = :id", Restaurant.class)
    .setParameter("id", greenBowlId)
    .setHint("jakarta.persistence.fetchgraph", em.getEntityGraph("Restaurant.menu"))
    .getSingleResult();                   // one SELECT with left join MenuItem

Spring Data JPA uses the same feature through the @EntityGraph annotation on a repository method.

2.7. Fixing It With Hibernate.initialize()

When the entity is already loaded, Hibernate.initialize() runs the second SELECT right away, while the transaction is still open. It costs one extra SELECT per association, whereas join fetch and an entity graph load the same data in the first query.

Restaurant restaurant = emf.callInTransaction(em -> {
  Restaurant r = em.find(Restaurant.class, greenBowlId);   // select ... from Restaurant ...
  Hibernate.initialize(r.getMenu());                        // select ... from MenuItem ... where m1_0.restaurant_id=?
  return r;
});
List<MenuItem> menu = restaurant.getMenu();   // [Pasta, Salad], after the commit

Calling Hibernate.initialize() after the transaction throws the same LazyInitializationException. Jakarta Persistence 3.2 added PersistenceUnitUtil.load(entity, “menu”) for the same job, but in Hibernate 7.4.11 it did not load the collection of our Restaurant entity (no SELECT, and the collection stayed uninitialized). So we stay with Hibernate.initialize().

2.8. Reading Only the Columns With a DTO Projection

A screen or a REST response often needs a few values, not managed entities. A DTO projection selects only those columns into a record, so the result holds no proxy or lazy collection that could throw later. For example, the record MenuLine holds the restaurant name, the item name and the price.

public record MenuLine(String restaurant, String item, BigDecimal price) {}

The JPQL query selects the three values and passes MenuLine.class as the result type.

List<MenuLine> lines = em.createQuery(
        "select r.name, m.name, m.price from MenuItem m join m.restaurant r "
            + "where r.id = :id order by m.name", MenuLine.class)
    .setParameter("id", greenBowlId)
    .getResultList();
// select r1_0.name,mi1_0.name,mi1_0.price from MenuItem mi1_0
//   join Restaurant r1_0 on r1_0.id=mi1_0.restaurant_id where r1_0.id=? order by mi1_0.name

// [MenuLine[restaurant=Green Bowl, item=Pasta, price=8.00], MenuLine[restaurant=Green Bowl, item=Salad, price=6.00]]

We can see that the SQL reads only r1_0.name, mi1_0.name and mi1_0.price. Hibernate 6 and later fill the record through its constructor from the selected values, whereas portable JPQL needs the constructor expression select new com.howtodoinjava.hibernate.lazy.MenuLine(r.name, m.name, m.price).

2.9. Lazy Loading and the N+1 Problem

The N+1 problem often shows up only in production. The “Top restaurants” page is fast on our laptop with 3 test restaurants. In production it lists 200 restaurants with their dish counts and takes several seconds, and the SQL log shows why. There is one query for the restaurants and 200 more after it, one per restaurant menu.

The N+1 problem is one query that loads a list of N entities, followed by one more query for each of them. For example, a loop that calls getMenu().size() on each restaurant from a list runs one MenuItem query per restaurant.

List<Restaurant> all = em.createQuery("select r from Restaurant r order by r.id", Restaurant.class)
    .getResultList();                     // select r1_0.id,r1_0.name from Restaurant r1_0 order by r1_0.id
all.forEach(r -> r.getMenu().size());     // select ... from MenuItem m1_0 where m1_0.restaurant_id=?  (3 times)

Switching to EAGER does not avoid the N+1 problem, because a JPQL query does not join an EAGER association. After the query, Hibernate runs a secondary SELECT for each referenced entity that is not loaded yet, so the three daily specials cause three MenuItem queries.

List<DailySpecial> specials = em.createQuery("select d from DailySpecial d", DailySpecial.class).getResultList();
// select ds1_0.id,ds1_0.item_id,ds1_0.weekday from DailySpecial ds1_0
// select ... from MenuItem mi1_0 where mi1_0.id=?    (3 times, once per special)

Hibernate statistics give the statement counts for our three restaurants. Batch fetching with hibernate.default_batch_fetch_size loads the menus of several restaurants with one IN query, whereas join fetch needs only one statement in total.

CodeStatements
Restaurant query, then getMenu().size() in a loop4 (1 + 3)
Same loop with hibernate.default_batch_fetch_size = 102 (the second one uses restaurant_id in (?,?,…))
select r from Restaurant r join fetch r.menu1
select d from DailySpecial d (EAGER item)4 (1 + 3)

3. Hibernate Lazy Loading FAQs

3.1. Why Is Open Session in View Not a Fix?

Because open session in view only moves the lazy loads out of the service transaction, and it does not remove them. It keeps the EntityManager open until the web response is rendered. Spring Boot turns it on by default (spring.jpa.open-in-view=true), which is why many Spring Boot applications never see a LazyInitializationException.

With open session in view, the lazy loads run in the view or during JSON serialization. Each lazy access still runs its own SELECT, so a page that lists 20 restaurants with their menus runs the same 1 + 20 statements as in section 2.9, only hidden from the service code. The EntityManager also stays open for the whole request, and the service method no longer shows which data the response needs.

So we set spring.jpa.open-in-view=false and load the data in the service method with one of the fixes from section 2.5 to section 2.8.

3.2. What Does hibernate.enable_lazy_load_no_trans Do?

The setting removes LazyInitializationException by opening a temporary session for each lazy access on a detached entity. With the detached restaurant from section 2.4, getMenu().size() returns 2 without an exception.

Restaurant detached = emf.callInTransaction(em -> em.find(Restaurant.class, greenBowlId));
int size = detached.getMenu().size();     // 2, no exception
// 1 new session opened, 1 SELECT, outside any transaction

Every lazy access opens its own session and runs its own query outside any transaction, so an N+1 loop opens N sessions. Hibernate documents this setting as unsafe, so we leave it off.

3.3. Why Is @Basic(fetch = FetchType.LAZY) Ignored?

Because Hibernate cannot put a proxy into a String or byte[] field, so a lazy simple column needs bytecode enhancement. The enhance goal of the Hibernate Maven plugin changes the compiled entity class, so that the getter of the field loads the column on first use. This matters when a table has a large column that the list screen never shows, such as a 2 MB menu PDF. Without enhancement, Hibernate loads the column with the rest of the row, as the description column of MenuItem shows.

@Basic(fetch = FetchType.LAZY)
private String description;

MenuItem pasta = em.find(MenuItem.class, pastaId);
// select mi1_0.id,mi1_0.description,mi1_0.name,... from MenuItem mi1_0 where mi1_0.id=?

The MenuCard entity holds a long text in a @Lob column, and we enhance only that class at build time with the enhance goal of hibernate-maven-plugin.

<plugin>
  <groupId>org.hibernate.orm</groupId>
  <artifactId>hibernate-maven-plugin</artifactId>
  <version>7.4.11.Final</version>
  <executions>
    <execution>
      <goals><goal>enhance</goal></goals>
      <configuration>
        <enableLazyInitialization>true</enableLazyInitialization>
        <fileSets>
          <fileSet>
            <directory>target/classes</directory>
            <includes><include>**/MenuCard.class</include></includes>
          </fileSet>
        </fileSets>
      </configuration>
    </execution>
  </executions>
</plugin>
@Lob
@Basic(fetch = FetchType.LAZY)
private String text;

MenuCard card = em.createQuery("select c from MenuCard c", MenuCard.class).getSingleResult();
// select mc1_0.id,mc1_0.title from MenuCard mc1_0
String text = card.getText();
// select mc1_0.text from MenuCard mc1_0 where mc1_0.id=?

We can see that the first query skips the text column, and getText() reads it with a second SELECT. An enhanced lazy column also throws LazyInitializationException when we first read it after the transaction.

3.4. Should I Switch to FetchType.EAGER to Avoid LazyInitializationException?

No, because EAGER loads the association in every use case, including the ones that never read it. Section 2.9 shows the cost, where a list query of DailySpecial rows ran 1 + 3 statements because of one EAGER @ManyToOne. Only a fetchgraph hint skips the association, and we must remember to add that hint to every query.

List<DailySpecial> specials = em.createQuery("select d from DailySpecial d", DailySpecial.class)
    .setHint("jakarta.persistence.fetchgraph", em.createEntityGraph(DailySpecial.class))
    .getResultList();                     // 1 SELECT, item is now a proxy

Keeping LAZY and fetching what each use case needs with join fetch or an entity graph is simpler.

3.5. Does join fetch Return Duplicate Parents?

No, not in Hibernate 6 and later. The SQL join returns one row per menu item (6 rows for our 3 restaurants), but Hibernate removes the duplicate parent entities from the result list.

List<Restaurant> restaurants = em.createQuery("select r from Restaurant r join fetch r.menu order by r.id", Restaurant.class)
    .getResultList();                     // 3 restaurants, 1 SELECT

Hibernate 5 returned each restaurant once per menu item, which is why older code writes select distinct r. With Hibernate 6 and later, we no longer need the distinct keyword for this.

4. Conclusion

Hibernate lazy loading reads an association from the database when our code first uses it, not when Hibernate loads the owner. A to-one field holds a proxy with only the id, and a collection field holds a PersistentBag that runs its SELECT on the first call that needs the elements, such as size() or a loop.

We map every association as LAZY and load what each use case needs inside the transaction with join fetch, an entity graph, Hibernate.initialize() or a DTO projection. Open session in view and hibernate.enable_lazy_load_no_trans only hide the extra queries, and EAGER runs them on every load.

5. References

Happy Learning !!

Source Code on Github

Leave a Comment

    • It seems we cannot. Lazy loading is applicable to associated entities or collections. @Formula is typically used to calculate the property value at the time of loading the entity.

  1. I was facing a related problem, I wanted to fetch an entity that has 6 entities which are Lazy loading type, and I was getting empty list.
    Thanks for this information.

  2. How can I implement eager loading and lazy loading in hbm files without using hibernate.

  3. Hello Lokesh, I have Portfolio and Business Unit Entities. I have used LAZY initialization here on Portfolio so that I will get all business unit of respective portfolio only when needed. However, now I see that when I do businessunit.getName(), lot of queries are executed to fetch businessunit details which is impacting performance.

    Can you let me know how to fix this issue. Marking as EAGER in portfolio also causes performance issue because it loads all the data on load of portfolio object.

  4. I am getting this exception: Any Help:

    job aborted for the user user1 due to exception: Version mismatch while modifying object. Id 1212
    job aborted for the user user2 due to exception: org.hibernate.exception.GenericJDBCException: could not prepare statement
    job aborted for the user user3 due to exception: org.hibernate.exception.GenericJDBCException: could not prepare statement
    encountered an error in Job: : javax.ejb.EJBTransactionRolledbackException: org.hibernate.exception.GenericJDBCException: could not prepare statement
    Caused by: javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: could not prepare statement
    Caused by: org.hibernate.exception.GenericJDBCException: could not prepare statement
    Caused by: java.sql.SQLException: IJ031070: Transaction cannot proceed: STATUS_MARKED_ROLLBACK

  5. Hi Lokesh,

    Can u give any realtime scenario about 1st and 2nd level cache…and when do we go for 1st and 2nd level cache

    • You cannot disable first level cache, it’s default with hibernate. Only understand how it works.
      Second level cache should be used when your database grows and you need to fetch same data set – multiple times or frequently.

  6. Hi Lokesh,

    I am just comparaing it to load v/s no-load theory.

    let’s say i have two sections of UI.In one of them i want to show child data along with parent data and in second UI i am just showing parent data.Then i should write two different calls at database level,one is loading child data and another in loading only parent data.

    How lazy/eager loading can helpful in this case because if i do lazy loading then it will consume more time in first case as it will query later rather than using joins etc. and if i use eager then it will consume time in case 2 as there is no need of child data.

    So the thing is ,I think writing two separate db calls is better than having lazy-eager loading.

    Please let me correct if i am wrong and tell me some cases where eager/lazy loading can be helpful.

    • Ankit, you brought up some good points and a valid usecase. In my understanding, lazy or eager loading depends on the basis of situation at hand. There is NO eternal law around it. You are right about two separate queries; a different one for each screen.

      Lazy loading can be helpful in cases where you are dealing with large amount of data (for small number of rows/data practically there is no visible difference and lazy loading should be termed as over-engineering). In most of the situations, we should be good to have data loaded eagerly without compromising any visible performance decrease.

      But if any entity in hibernate has lost of references to other entities, and you really are not going to use that information in most of the scenarios then making them lazy loaded make sense. First could be tiny performance improvement; but second is definitely less number of objects loaded into JVM memory and that is very precious resource in any system. Lazy loading is another way to bring down unused objects inside JVM so use it.

      Let’s assume we had a User object that has 10 different relationships to other objects. Let’s also say that you have a user that’s trying to login and you just want to check to see if their username and password is correct and matches what’s stored in the database. Would you really want to load ALL 10 relationships and NOT make use of ANY of those loaded relationships? I will not do this.

  7. Hi, and thanks for this post.

    Could you bring more details and a short example in the last part of this “how to”, about the LazyInitializationException:

    The part is :

    A little bit of code here would be very helpfull.

    best regards from France

  8. Hi, and thanks for this post.

    Could you bring more details and a short example in the last part of this “how to”, about the LazyInitializationException:

    The part is :

    “The cure is to ensure either that the entity is made persistent again by attaching it to a session or that all of the fields that will be required are accessed (so they are loaded into entity) before the entity is detached from the session.”

    A little bit of code here would be very helpfull.

    best regards from France

  9. What I wish is to switch from lazy to eager fetching on different parts of my code, depending on the situation.

    E.g.
    Person {
    String name;
    Department dept;
    }

    For example, if I wish to display a single record on screen, lazy loading is great. But when we wish to display a list, eager fetch is more efficient. Because it can join and retrieve all records in one Go. If it’s lazy loading, then you retrieve multiple times. (E.g. department table is queried 10 times if we retrieve 10 records from person table)

  10. HI Lokesh ,
    Thanks for sharing such a good concept.I am Working on Spring Hibernate and always struggling with jacson mapper exception -no session kind of exception in following case for eg.
    Please see the below example
    1.MY POJOs
    Class State{
    private String name;

    @OneToMany( mappedBy = “state”, fetch = FetchType.LAZY )
    private Set products;
    ///getter and setters
    }
    Class Disrict{
    private String name;

    @ManyToOne
    private State state
    ///getter and setters
    }

    2.My Dao
    Class MydaoImpl implemnets MyDao{

    public List getStateList(){
    List list=null;
    try{
    Session session=HibernateUtil.getSessionFactory.openSession();
    list=session.createCirteria(State.class).list();
    }
    catch(){
    }
    return list;
    }
    }
    3.My service

    @Transactional
    Class MyServiceImpl implemnets MyService{
    @Autowired MyDao mydao;
    public List getStateList(){
    return maydao.getStateList();
    }
    }

    4.finally my Controller
    Class MyController{
    @autowired MyService myservice;

    @Requestmapping(“/getSates”)
    public @ResponseBody List getAllStates(){
    return myService.getStateList();
    }

    so When I am hitting Url for stateList ..it throws jackson exception ..like no session and i m not able to get the state List in ajax call.

    Please tell me the possible solution for this.I searched and found about @JsonIgnore annotation to be used with @OneToMany Annotation.
    what is the best way to do this.
    thanks
    Hemant

Comments are closed.

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.