Java.Hibernate.Middle.Examples where batch work and where not

let’s put it all together with two clean, full examples side by side: one where batch fetching works (correct) and one where it doesn’t work (incorrect).

I’ll give you them both without extra comments, so you can clearly compare.


✅ Working example — batch fetching will work (one transaction/session)

@Service
@RequiredArgsConstructor
public class AuthorService {

    private final EntityManager em;

    @Transactional // single transaction/session for entire method
    public void loadAuthorsAndBooksInSingleSession() {
        List<Author> authors = em.createQuery(
            "FROM Author", Author.class
        ).setMaxResults(20).getResultList();

        for (Author author : authors) {
            List<Book> books = author.getBooks();
            System.out.println("Author: " + author.getName() + ", books: " + books.size());
        }
    }
}

🚫 Failing example — batch fetching will NOT work (multiple transactions/sessions)

@Service
@RequiredArgsConstructor
public class AuthorService {

    private final EntityManager em;

    public void loadAuthorsAndBooksInMultipleSessions() {
        List<Author> authors = em.createQuery(
            "FROM Author", Author.class
        ).setMaxResults(20).getResultList();

        for (Author author : authors) {
            loadBooksInSeparateTransaction(author);
        }
    }

    @Transactional // each call creates a new transaction/session
    public void loadBooksInSeparateTransaction(Author author) {
        List<Book> books = author.getBooks();
        System.out.println("Author: " + author.getName() + ", books: " + books.size());
    }
}

✅ Key difference

  • First example: @Transactional wraps loading authors and accessing their lazy collections in a single session → Hibernate sees all lazy proxies together → batch fetching works.
  • Second example: each call to loadBooksInSeparateTransaction() opens a new session → proxies are isolated → batch fetching can’t happen → you’ll get separate queries for each author’s collection.

✅ Working example (single session, batching works)

Given @BatchSize(size = 10) on Author.books and loading 20 authors:

SQL executed:

-- 1) Load authors (all 20 in one query)
select * from authors limit 20;

-- 2) Load books for first batch of 10 authors in a single query
select * from books where author_id in (1,2,3,4,5,6,7,8,9,10);

-- 3) Load books for second batch of 10 authors in a single query
select * from books where author_id in (11,12,13,14,15,16,17,18,19,20);

✔ Total queries: 3
✔ Hibernate groups proxies for 10 authors’ books per batch → batch fetching works.

🚫 Failing example (multiple sessions, batching fails)

Again with 20 authors and @BatchSize(size = 10), but now each author’s books are loaded in a separate transaction/session:

SQL executed:

-- 1) Load authors (all 20 in one query)
select * from authors limit 20;

-- 2) Load books for each author in separate queries:
select * from books where author_id = 1;
select * from books where author_id = 2;
select * from books where author_id = 3;
...
select * from books where author_id = 20;

✔ Total queries: 21
✔ Hibernate sees proxies for only one author at a time → no batching happens → classic N+1 problem.

🔥 Key difference:

  • When authors and lazy collections live in the same session → Hibernate groups lazy proxies → batch fetching combines them into fewer queries.
  • When proxies are spread across multiple sessions → each proxy loads separately → no batching → N+1.
This entry was posted in Без рубрики. Bookmark the permalink.

Leave a Reply

Your email address will not be published.