Why a Fast API Suddenly Became 25x Slower: Debugging a Hidden Hibernate N+1 Query Problem?

  • Home
  • Why a Fast API Suddenly Became 25x Slower: Debugging a Hidden Hibernate N+1 Query Problem?

Why a Fast API Suddenly Became 25x Slower: Debugging a Hidden Hibernate N+1 Query Problem?

June 24, 2026 asha Comments Off
Backend performance optimization

For a long time, one of our APIs was performing exceptionally well. Every request completed in approximately 200 milliseconds, and the application handled traffic smoothly without any noticeable issues.

Then, almost unexpectedly, the API became dramatically slower.

Response times increased from around 200 milliseconds to nearly 5 seconds. Users started experiencing delays, and overall application performance suffered significantly.

What made the situation even more confusing was that nothing major had changed.

There were no recent feature releases, no infrastructure modifications, no database migrations, and no spikes in server utilization. On the surface, everything appeared normal.

GET YOUR FREE CONSULTATION FOR YOUR BUSINESS TODAY!





    The Investigation Begins

    Like most developers facing a performance issue, my first assumption was that the database was responsible.

    I started by checking the usual suspects:

    Everything looked healthy.

    The database was responding quickly, server resources were well within limits, and there were no obvious bottlenecks.

    Yet the API remained painfully slow.

    At this point, I realized the problem was likely hidden somewhere deeper within the application itself.

    Understanding Hibernate's Hidden Cost

    To investigate further, I enabled Hibernate SQL logging and started reviewing the generated queries.

    What I discovered was surprising.

    A single API request was generating hundreds of SQL queries.

    The endpoint should have executed only one or two queries. Instead, Hibernate was repeatedly issuing additional queries for related entities.

    The root cause was the classic N+1 Query Problem.

    What is the N+1 Query Problem?

    Consider a simple example involving Users and Orders.

    @Entity
    public class User {

    @OneToMany(mappedBy = “user”, fetch = FetchType.LAZY)
    private List<Order> orders;
    }

    Now imagine retrieving all users:

    List<User> users = userRepository.findAll();

    Later, the application accesses the orders for each user:

    for (User user : users) {
        System.out.println(user.getOrders().size());
    }

    Query 1

    SELECT * FROM users;

    Then for every user:

    Query 2

    SELECT * FROM orders WHERE user_id = 1;

    Query 3

    SELECT * FROM orders WHERE user_id = 2;

    Query 4

    SELECT * FROM orders WHERE user_id = 3;

    And so on.

    If there are 100 users, Hibernate executes:

    • 1 query to retrieve users
    • 100 additional queries to retrieve orders

    Total: 101 queries

    Instead of only one or two queries.

    This is known as the N+1 Query Problem.

    Why It Becomes a Performance Disaster

    Every database query has overhead.

    Each query requires:

    • Database parsing and execution
    • Network communication
    • Memory allocation
    • Connection handling
    • Result processing

    A single query may execute in milliseconds.

    Hundreds of unnecessary queries create cumulative delays that quickly add up.

    This was exactly what happened in our case.

    As the dataset grew, the hidden queries multiplied.

    The API that previously responded in around 200ms gradually slowed until requests were taking nearly 5 seconds.

    How We Fixed It

    1. Using JOIN FETCH)

    Instead of allowing Hibernate to lazily load related entities one by one, we fetched everything in a single query.

    @Query(“SELECT u FROM User u JOIN FETCH u.orders”)List<User> findAllUsersWithOrders();

    Hibernate now retrieves users and orders together.

    This dramatically reduces the number of database round trips.

    2. Using EntityGraph

    Another clean solution is EntityGraph.

    @EntityGraph(attributePaths = {“orders”})List<User> findAll();

    EntityGraph allows us to explicitly define which relationships should be loaded eagerly for a specific operation.

    Benefits:

    • Cleaner code
    • Better maintainability
    • Reduced query count
    • Improved readability

    3. Reviewing Lazy Loading

    Lazy loading is not bad.

    In fact, it’s often the right choice.

    The problem occurs when lazy-loaded collections are accessed inside loops.

    We reviewed our codebase and identified areas where lazy-loaded relationships were triggering unnecessary queries.

    Fetching required data upfront eliminated many hidden database calls.

    4. Enabling Batch Fetching

    We also enabled Hibernate batch fetching

    spring.jpa.properties.hibernate.default_batch_fetch_size=50

    Batch fetching allows Hibernate to load multiple entities in groups rather than issuing separate queries for each record.

    This significantly reduces database communication overhead

    The Results

    The improvements were immediate.

    MetricBefore FixAfter Fix
    Response Time~5 seconds~200 ms
    Queries Executed100+1-2
    Database LoadHighLow
    User ExperiencePoorFast

    The API returned to its original performance levels, and the system once again delivered fast and consistent responses.

    Key Lessons Learned

    1. Clean Code Doesn't Always Mean Efficient Code

    An application can look perfectly designed while still generating excessive database traffic behind the scenes.

    2. Query Count Matters

    Two APIs may have identical response times today but behave very differently as data volumes increase.

    Monitoring query count is just as important as monitoring response time

    3. ORM Frameworks Are Powerful but Not Magic

    Hibernate simplifies development tremendously, but developers still need to understand how it generates SQL queries.

    Ignoring what’s happening underneath can lead to serious performance issues.

    4. Small Problems Become Big Problems at Scale

    What seems insignificant with ten records can become a major bottleneck with thousands or millions of records.

    A Simple Habit That Prevents Future Problems

    Since this experience, I’ve adopted a simple rule whenever building or reviewing an API:

    "How many database queries will this endpoint execute?"

    That single question has helped identify performance issues long before they reach production.

    GET YOUR FREE CONSULTATION FOR YOUR BUSINESS TODAY!





      Final Thought

      Performance issues are rarely caused by one big mistake.

      More often, they come from small inefficiencies that quietly accumulate over time until they become impossible to ignore.

      The next time an API slows down unexpectedly, don’t just look at response times.

      Look at the number of database queries being executed.

      The answer might surprise you.


      🚀 Have you ever encountered an N+1 Query Problem in Hibernate, Spring Boot, or another ORM? Share your experience in the comments

       

      Contact:

       ☎️ RedMinds: +91 9550283428, +91 9010302031
       🔗 LinkedIn: https://lnkd.in/gTFG4UME
       📧 Email: 21@redmindstech.com
       🌐 Website: redmindstech.com
       📘 Facebook: https://lnkd.in/g8aV4uz7
       📸 Instagram: https://lnkd.in/gDUtSGA4 
        X Twitter: https://lnkd.in/gXBgUBgw

      What is the Hibernate N+1 Query Problem?

      It’s a performance issue where one initial query triggers multiple additional queries for related data, resulting in N+1 total queries.

       

      By enabling Hibernate SQL logging and reviewing the generated SQL queries.

      It increased from around 200 ms to nearly 5 seconds.

       

      Share Your Details, and We’ll Reach Out to Discuss Your Goals