Java.Hibernate.Spring.Middle.Self invocation

There is one of the most common (and subtle) pitfalls of Spring’s @Transactional: self-invocation, which can silently break transactional (and other proxy-based) behavior.


🔎 What is self-invocation?

It’s when a method in a Spring bean calls another method on the same bean instance, like:

@Service
public class MyService {
    @Transactional
    public void outer() {
        inner(); // self-invocation
    }

    @Transactional
    public void inner() {
        ...
    }
}

Here, calling inner() from outer() happens directly on this, bypassing the Spring proxy → annotations like @Transactional on inner() are ignored.

✅ How to work around self-invocation?

✅ 1) Extract to another Spring bean

Move the inner() method to a separate @Service or @Component:

@Service
public class InnerService {
    @Transactional
    public void inner() {
        ...
    }
}

@Service
@RequiredArgsConstructor
public class OuterService {
    private final InnerService innerService;

    @Transactional
    public void outer() {
        innerService.inner(); // goes through Spring proxy → transactional works
    }
}

✔ Recommended for clean separation of concerns.

✅ 2) Inject self-proxy

Inject the current bean as a proxy into itself and call methods through it:

@Service
public class MyService {

    @Autowired
    private MyService selfProxy;

    @Transactional
    public void outer() {
        selfProxy.inner(); // calls through proxy → transactional applies
    }

    @Transactional
    public void inner() {
        ...
    }
}

✔ Works, but can be confusing and couples the class to itself.

✅ 3) Use AopContext (advanced)

Enable exposeProxy=true on your @EnableTransactionManagement configuration, then get the proxy dynamically:

@EnableTransactionManagement(exposeProxy = true)
@Configuration
public class AppConfig {}

@Service
public class MyService {

    @Transactional
    public void outer() {
        ((MyService) AopContext.currentProxy()).inner(); // calls through proxy
    }

    @Transactional
    public void inner() {
        ...
    }
}

✔ Handy but a bit hacky; avoid unless you really need it.

🔥 Bottom line:

The best practice is usually extracting methods to another Spring bean and calling through it → clear, maintainable, and doesn’t rely on Spring-specific tricks.

Posted in Без рубрики | Comments Off on Java.Hibernate.Spring.Middle.Self invocation

Java.Hibernate.if we have transactional inside transactional how will it work ?

✅ When you call a @Transactional method from another @Transactional method in the same bean (or same Spring proxy):

  • By default, Spring uses propagation REQUIRED, which means:
    • The inner @Transactional method joins the existing transaction of the outer method.
    • There’s still only one physical transaction and one session.
    • Both methods share the same first-level cache/persistence context → batch fetching can work as long as the session stays open.

@Transactional
public void outerMethod() {
    List<Author> authors = em.createQuery("FROM Author", Author.class).getResultList();
    for (Author author : authors) {
        innerMethod(author); // transactional inside transactional
    }
}

@Transactional
public void innerMethod(Author author) {
    List<Book> books = author.getBooks(); // shares same transaction/session with outerMethod
    ...
}

✔ In this case, since both methods are called within the same proxy, and default propagation is REQUIRED, innerMethod runs inside the same transaction/session → batch fetching can work.

🚨 BUT there’s a catch:

Self-invocation → if you call this.innerMethod(...) inside the same class, Spring’s proxy-based AOP will not apply the transactional behavior to the inner method, and your @Transactional on innerMethod will be ignored entirely!

✅ To make nested transactions effective, you need:

  • Either the calls to go through a Spring proxy (e.g., call methods on another bean),
  • Or use AOP-aware mechanisms like ApplicationContext.getBean(ThisClass.class).innerMethod(...)

🟢 Summary of key behaviors:
1️⃣ Transactional inside transactional, same proxy, default propagation (REQUIRED) → inner joins outer transaction → same session → batching can work.
2️⃣ Transactional with different propagation (e.g., REQUIRES_NEW) → inner opens new transaction → new session → batching won’t work across them.
3️⃣ Transactional inside same class by self-call → ignored → inner @Transactional won’t apply unless the call goes through Spring’s proxy.

🔥 Bottom line:

In Spring, nested @Transactional methods with propagation REQUIRED share the same transaction/session, enabling batch fetching; but self-invocation can silently break transactional behavior.

Posted in Без рубрики | Comments Off on Java.Hibernate.if we have transactional inside transactional how will it work ?

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.
Posted in Без рубрики | Comments Off on Java.Hibernate.Middle.Examples where batch work and where not

Java.Hibernate.Middle.What means proxies, when we speak of lazy loading?

when I say “proxies,” I do mean entities, but specifically lazy-loaded entities or collections that Hibernate wraps in a proxy object.

Here’s what that means:


🔎 What is a proxy in Hibernate?

  • A proxy is a lightweight placeholder object Hibernate creates instead of immediately loading an entity or collection.
  • When you mark an association as FetchType.LAZY, Hibernate doesn’t load the actual entity right away. Instead, it creates a proxy object that:
    • Looks like your entity in Java (same class or a subclass generated by Hibernate).
    • Only fetches the real data when you first call a getter or otherwise access the proxy.

✅ Example:

@Entity
public class Author {
    @OneToMany(mappedBy = "author", fetch = FetchType.LAZY)
    private List<Book> books; // Hibernate will create a proxy for this collection
}

When you load an Author but don’t touch author.getBooks(), Hibernate just gives you a proxy collection representing books.
When you later access getBooks(), Hibernate triggers a SQL query to load the books.

🔹 So when I say:

Batch fetching works by grouping multiple proxies of the same type waiting for initialization…

I mean:

  • The placeholder proxy objects Hibernate gives you instead of real entities or collections.
  • When you access multiple of these proxies in the same session, Hibernate batches their SQL queries.

🚨 Key point:

  • All lazy-loaded entities or collections are proxies until you access them.
  • If you eagerly load entities, they’re real objects immediately → no proxies → batch fetching has no work to do.
Posted in Без рубрики | Comments Off on Java.Hibernate.Middle.What means proxies, when we speak of lazy loading?

Java.Hibernate.Middle.If i load entity in first level cache and i have 2 level cache, where my entity will live?

✅ What happens when you load an entity?

When you fetch an entity with Hibernate (e.g., em.find() or HQL query), the lookup happens in order:

1️⃣ First-level cache (1st-level) — also called the persistence context or session cache:

  • Every open EntityManager or Hibernate Session has its own first-level cache.
  • When you load an entity during a session, it’s stored here.
  • Repeated accesses to the same entity within the same session return the cached instance without hitting the database or 2nd-level cache.

2️⃣ Second-level cache (2nd-level) — optional, shared across sessions:

  • Configured explicitly with a provider (e.g., Ehcache, Infinispan).
  • Stores entities between sessions so they can be reused across transactions or requests.
  • If an entity isn’t in the first-level cache but is found in the second-level cache, Hibernate loads it from there instead of querying the database.

3️⃣ Database

  • If the entity is in neither the first-level nor the second-level cache, Hibernate executes SQL to load it.

🔎 Where will the entity “live”?

  • Once loaded, the entity always lives in the first-level cache of the current session until that session ends (or you evict the entity from the session).
  • If second-level caching is enabled and properly configured for the entity:
    • When you first load the entity from the database, Hibernate will put a copy into the second-level cache.
    • Later sessions can check the second-level cache and reuse that entity without querying the DB.

✅ So the answer:

After loading, the active instance of the entity “lives” in the 1st-level cache, and a copy may live in the 2nd-level cache for reuse across sessions.


🔥 Example flow:

  • Session 1:
    • Load entity with ID=1 → 1st-level cache is empty → 2nd-level cache is checked.
    • If not in 2nd-level → DB query → entity added to both 1st-level and 2nd-level cache.
  • Still in Session 1:
    • Access entity again → served from 1st-level cache.
  • Session 2:
    • Load entity with ID=1 → 1st-level cache (of Session 2) is empty → 2nd-level cache is checked → if found, loaded into Session 2’s 1st-level cache.

🚨 Key points:

✔ First-level cache is mandatory, always enabled, and tied to the lifecycle of the current session.
✔ Second-level cache is optional, shared across sessions, and must be explicitly configured per entity or query.
✔ Hibernate never skips 1st-level cache; it always checks it before looking at the 2nd-level cache or database.

Posted in Без рубрики | Comments Off on Java.Hibernate.Middle.If i load entity in first level cache and i have 2 level cache, where my entity will live?

Java.Hibernate.Middle.When batch will not work ?

🚫 1. Detached entities or closed session

  • Batch fetching works only if multiple lazy proxies are accessed within the same persistence context (i.e., the same open Hibernate Session or JPA EntityManager).
  • If you load entities in one transaction but access their lazy associations later, outside of that session, you’ll get LazyInitializationException instead of batch fetching.

🚫 2. Single proxy accessed

  • Batch fetching triggers only if Hibernate sees multiple proxies of the same type waiting for initialization.
  • If you access just one lazy association, batching has nothing to batch → it’s still a single query.

🚫 3. Associations already eagerly loaded

  • If you fetch an association with JOIN FETCH or it’s configured with FetchType.EAGER, there’s no lazy loading left → batch fetching has no effect.

🚫 4. Mixed session contexts

  • If you scatter your entity access across multiple transactions or EntityManagers, batch fetching can’t group proxies because it only considers proxies in the same session.

🚫 5. Proxies of different types

  • Batch fetching only batches same-type lazy proxies.
    • E.g., if you have 20 Author proxies and 20 Publisher proxies, they’re batched separately — not together.
    • So if you only have a few of each type, batching may have limited impact.

🚫 6. Direct queries ignoring proxies

  • If you manually run queries on child entities (e.g., SELECT b FROM Book b WHERE ...) instead of accessing them through lazy proxies → batch fetching never triggers.

🚫 7. Using DTO projections

  • If you bypass entity mappings entirely (e.g., mapping query results to DTOs), Hibernate doesn’t manage proxies → batch fetching doesn’t apply.

⚠️ Summary:
Batch fetching works only on lazy proxies accessed in the same session, and only if there’s more than one proxy of the same type to batch together.

Posted in Без рубрики | Comments Off on Java.Hibernate.Middle.When batch will not work ?

Java.Hibernate.Middle.What are batch fetching and how do you enable it?

batch fetching is a great way to avoid the N+1 problem without forcing eager loading. Let’s break it down clearly:

🔎 What is batch fetching?

When you use lazy loading, Hibernate loads collections or associated entities on demand — but by default, it issues 1 query per entity → classic N+1 problem.

Batch fetching lets Hibernate load multiple entities or collections in batches when one is accessed, reducing the number of queries.

For example:

  • Without batch fetching: loading 100 lazy Author.books → 100 separate queries.
  • With batch fetching size 10: loading those 100 → only ~10 queries (each loads 10 collections at once).

✅ How to enable batch fetching

You do it in your entity mappings using Hibernate’s @BatchSize annotation or globally in configuration:

1️⃣ Per-association: @BatchSize

For an entity or collection field:

@Entity
public class Author {
    @OneToMany(mappedBy = "author", fetch = FetchType.LAZY)
    @BatchSize(size = 10) // Hibernate will load books for 10 authors at once
    private List<Book> books;
}

Or on an entity to affect all its lazy references:

@Entity
@BatchSize(size = 10)
public class Book {
    // ...
}

2️⃣ Global setting: Hibernate property

If you want batch fetching to apply to all lazy associations in your app, set in your config (e.g., application.properties):

spring.jpa.properties.hibernate.default_batch_fetch_size=10

Or directly in Hibernate:

hibernate.default_batch_fetch_size=10

🚨 Important notes:

✔ Batch fetching ≠ eager fetching — it keeps associations lazy, but batches the loads when needed.
✔ Works only if you access multiple lazy proxies of the same type in the same session.
✔ Doesn’t change JPQL behavior — it applies when Hibernate resolves proxies or collections.
✔ Very helpful for reducing query count in web pages/tables showing multiple entities with lazy fields.

🔎 Example:
You load 20 authors, then access author.getBooks() in a loop — instead of 20 queries, Hibernate will do 2 queries (if batch size is 10).

🛠 Example scenario

You have:

  • Entity Author
  • Each author has many lazy-loaded Book entities (@OneToMany(fetch = LAZY)).

❌ Without batch fetching

List<Author> authors = entityManager.createQuery(
    "SELECT a FROM Author a", Author.class
).getResultList();

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

If authors.size() == 20, Hibernate will run:

  • 1 query for authors,
  • 20 separate queries for each author’s books,
  • total: 21 queries.

✅ With batch fetching

First, enable batching with @BatchSize on the collection:

@Entity
public class Author {
    @OneToMany(mappedBy = "author", fetch = FetchType.LAZY)
    @BatchSize(size = 5) // fetch 5 authors’ books in one query
    private List<Book> books;
}

Then, run the same code:

List<Author> authors = entityManager.createQuery(
    "SELECT a FROM Author a", Author.class
).getResultList();

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

Now, Hibernate groups lazy loads into batches of 5:

  • 1 query for authors,
  • 4 queries for books (because 20 authors / batch size 5 = 4 batches),
  • total: 5 queries, not 21.

🔎 SQL logs comparison

Without batch:

-- Query 1: fetch authors
SELECT * FROM authors;

-- Then for each author (20 times):
SELECT * FROM books WHERE author_id = ?;
...

With batch size 5:

-- Query 1: fetch authors
SELECT * FROM authors;

-- Batch 1: books for authors 1-5
SELECT * FROM books WHERE author_id IN (?, ?, ?, ?, ?);

-- Batch 2: authors 6-10
SELECT * FROM books WHERE author_id IN (?, ?, ?, ?, ?);
...
(total 4 batches)

✅ Why it matters

  • Greatly reduces database round trips.
  • Keeps your associations lazy (good for flexibility) but avoids N+1 problem.
Posted in Без рубрики | Comments Off on Java.Hibernate.Middle.What are batch fetching and how do you enable it?

Java.Hibernate.Middle.How do you configure fetch type for an association?

You configure the fetch type for an association directly on your entity mapping using the fetch attribute of @OneToOne, @OneToMany, @ManyToOne, or @ManyToMany. Here’s how:

✅ For @OneToOne and @ManyToOne

By default, these are EAGER, but you can override:

@Entity
public class Order {
    @ManyToOne(fetch = FetchType.LAZY) // Explicitly set LAZY
    private Customer customer;
}

✅ For @OneToMany and @ManyToMany

By default, these are LAZY, but you can override:

@Entity
public class Customer {
    @OneToMany(mappedBy = "customer", fetch = FetchType.EAGER) // Explicitly set EAGER
    private List<Order> orders;
}

📌 Syntax summary:

@OneToOne(fetch = FetchType.LAZY)
@ManyToOne(fetch = FetchType.LAZY)
@OneToMany(fetch = FetchType.EAGER)
@ManyToMany(fetch = FetchType.LAZY)

🚨 Important notes:

  • EAGER means the associated entity or collection is always loaded immediately with the parent entity.
  • LAZY means the association is fetched only when you access it (via proxy initialization).
  • Hibernate best practice: keep associations LAZY by default and use JOIN FETCH or EntityGraph for specific queries needing eager loading → this avoids unexpected performance problems like N+1 or massive joins.

🔥 Example with both types:

@Entity
public class Author {
    @OneToMany(mappedBy = "author", fetch = FetchType.LAZY) // collection, defaults to LAZY
    private List<Book> books;

    @OneToOne(fetch = FetchType.EAGER) // single association, defaults to EAGER
    private Biography biography;
}
Posted in Без рубрики | Comments Off on Java.Hibernate.Middle.How do you configure fetch type for an association?

Java.Hibernate.Middle.Dublicating parents when fetch join

🔎 The root cause: how SQL JOINs work with one-to-many

Imagine you have:

  • Author (id=1) with 3 Books (Book A, Book B, Book C).

When you do a JOIN to get authors and their books:

SELECT a.*, b.* FROM authors a
JOIN books b ON a.id = b.author_id

Your SQL result will look like:

| author_id | author_name | book_id | book_title |
|----------|-------------|---------|------------|
| 1        | John Smith  | 101     | Book A     |
| 1        | John Smith  | 102     | Book B     |
| 1        | John Smith  | 103     | Book C     |

Notice what happens:

  • The same author appears 3 times — once for each book — because that’s how SQL JOINs work: they produce a row per combination of parent-child.

🚨 Without DISTINCT in JPQL

Hibernate reads those rows and reconstructs objects.
But if your query is:

SELECT a FROM Author a JOIN FETCH a.books

Hibernate sees 3 rows, each with an Author, and by default adds all of them to the result list → so your List<Author> will have 3 elements, each seemingly the same Author (but possibly different object instances pointing to the same DB row).
That’s why you get duplicates.

✅ With DISTINCT in JPQL

When you write:

SELECT DISTINCT a FROM Author a JOIN FETCH a.books

Hibernate collects all Authors from the result rows, but then deduplicates them in-memory based on their IDs, so you only get one Author instance in your List<Author>, no matter how many rows the JOIN produced.

🔎 Key point:

  • The duplication isn’t a bug — it’s the nature of SQL’s one-to-many joins.
  • But it becomes a problem when you want a clean list of unique parents in your Java collection.

✅ Always use DISTINCT when JOIN FETCHing a collection
It avoids surprises like:

  • lists containing dozens or hundreds of duplicates of the same parent entity,
  • bugs where List.size() is misleading,
  • unexpected UI issues (e.g., table rows showing duplicates).
Posted in Без рубрики | Comments Off on Java.Hibernate.Middle.Dublicating parents when fetch join

Java.Hibernate.Middle.How can you avoid the N+1 select problem?

✅ 1. Use JOIN FETCH in JPQL/HQL

This is the most direct way:

List<Author> authors = em.createQuery(
    "SELECT DISTINCT a FROM Author a JOIN FETCH a.books", Author.class
).getResultList();

This forces Hibernate to fetch authors and their books in one SQL query.

✅ 2. Use EntityGraph (JPA 2.1+)

If you want to avoid hard-coding fetch joins into your queries, JPA’s EntityGraph allows you to dynamically specify what to eagerly load:

EntityGraph<?> graph = em.createEntityGraph(Author.class);
graph.addAttributeNodes("books");

List<Author> authors = em.createQuery("FROM Author", Author.class)
    .setHint("javax.persistence.fetchgraph", graph)
    .getResultList();

✅ 3. Batch fetching (Hibernate-specific)

If you don’t want to fetch everything eagerly but want to reduce the number of queries, Hibernate’s @BatchSize lets you fetch collections in batches:

@Entity
public class Author {
    @OneToMany(mappedBy = "author")
    @BatchSize(size = 10) // fetch 10 authors’ collections at once
    private List<Book> books;
}

This reduces N queries to ~N/10 queries.

✅ 4. Set fetch mode to FetchType.EAGER carefully

In your entity mappings, you can switch collections to EAGER, but be careful:

@OneToMany(fetch = FetchType.EAGER)
private List<Book> books;

EAGER fetching can help avoid N+1 but might cause performance issues or Cartesian product explosions if not used wisely — so prefer JOIN FETCH or EntityGraph over global eager fetching.

✅ 5. Profile with Hibernate statistics or SQL logs

Before optimizing blindly, check if N+1 happens in your actual queries:

spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.generate_statistics=true

This way, you can see how many queries are executed.

🚨 Important note:

  • Using JOIN FETCH on a collection may cause duplicate parent rows in the result set, so always add DISTINCT to avoid getting the same parent entity multiple times.

🔎 Summary:

  • Use fetch joins or EntityGraph for single-query loading.
  • Consider batch fetching if you want to keep lazy loading but reduce query count.
  • Avoid eager fetching as a blanket solution; it can hurt performance in other scenarios.
Posted in Без рубрики | Comments Off on Java.Hibernate.Middle.How can you avoid the N+1 select problem?