Why I Regret Using Hibernate's EAGER Fetching
It was a few years ago. I was working at a Ride-Hailing company. We were rebuilding the "My Rides" history screen. The requirements were seemingly simple: show the user their past 50 rides, including the pickup and drop-off points.
The Mistake
Our data model had a Ride entity and a list of Waypoints. I knew the UI always needed to map the route, so I thought I was being efficient by automating the fetch:
@Entity
public class Ride {
@OneToMany(fetch = FetchType.EAGER)
private List<Waypoint> waypoints;
// ...
}
On my local machine, with a seeded database of 5 rides, it was instant. I shipped it.
The Failure Then came the evening rush hour. Thousands of users opened the app.
The database CPU spiked to 99%. Queries began queuing. The entire booking service started timing out. People couldn't book cars.
I checked the logs and saw the N+1 Nightmare. When a user with 50 past rides opened their history, Hibernate didn't run 1 query. It ran 51 queries.
Multiply that by 10,000 concurrent users. We were hitting the database with 500,000 queries per second.
The Fix We hot-fixed it by replacing EAGER with a specific JPQL query using JOIN FETCH.
SELECT r FROM Ride r
LEFT JOIN FETCH r.waypoints
WHERE r.userId = :userId
The Lesson Convenience is the enemy of Scale. Hibernate's EAGER fetching is a landmine. If you don't know exactly how many SQL statements your code generates, you aren't engineering—you're guessing.