🚀 Day 7/45 – Backend Engineering (JPA & Hibernate) Most performance issues in backend apps don’t come from logic — they come from how data is fetched from the database. Today I focused on one of the most common JPA pitfalls: ❗ The N+1 Query Problem 💡 What happens: You fetch a list of entities (1 query) For each entity, JPA triggers another query for related data Result → 1 + N queries 👉 This can silently kill performance in production. 🔍 Example: Fetching a list of users and their orders: 1 query for users N queries for orders ❌ 🔧 Fix: Use JOIN FETCH Use Entity Graphs Choose fetch type wisely (LAZY vs EAGER) 🛠 Practical: Tested API behavior with lazy loading and saw how queries multiplied without optimization. 📌 Real-world impact: Ignoring this leads to: Slow APIs High DB load Poor scalability 🔥 Takeaway: ORMs don’t guarantee performance. You must understand what queries are actually being executed. Currently building a production-ready backend system — sharing real backend lessons daily. https://lnkd.in/gJqEuQQs #Java #SpringBoot #Hibernate #BackendDevelopment #Performance #LearningInPublic
JPA Performance Pitfall: N+1 Query Problem
More Relevant Posts
-
Spring Boot's OSIV default turns your JSON serializer into a hidden query engine I'm building a personal finance platform with Spring Boot 3.5 + Java 21. The first thing I disabled was Open Session in View. Spring Boot ships with spring.jpa.open-in-view=true. That means Hibernate keeps a database connection open through the entire HTTP request - including JSON serialization. When Jackson walks your entity graph to build a response, every uninitialized lazy relationship triggers a database query. Your serializer is now executing SQL. In a load test with HikariCP's default pool of 10 connections and around 150 concurrent requests, this is where things break. Each request holds a connection for the full request lifecycle instead of just the service layer. The pool exhausts, threads queue up, and response times spike. The tricky part is that it works fine in dev when you're the only user. Disabling OSIV forces you to think about what data you actually need. You fetch it explicitly in the service layer with JOIN FETCH or projections, map it to a DTO, and the connection goes back to the pool before serialization even starts. It's more code upfront but the data flow becomes visible instead of hidden behind proxy magic. The second thing I changed was ddl-auto. Hibernate's update mode can generate schema changes automatically, but it can't rename columns, drop unused indexes, or migrate data. It produces a schema that looks right but drifts from what you intended. I use validate with Flyway migrations instead - every schema change is an explicit, versioned SQL file. If the code and the database disagree, the app refuses to start rather than silently diverging. These two defaults share the same problem. They hide complexity that surfaces as production issues. OSIV hides query execution. ddl-auto update hides schema drift. In both cases, making the behavior explicit costs more effort early but removes an entire class of debugging later. #SpringBoot #Java #BuildInPublic #BackendDevelopment
To view or add a comment, sign in
-
🚀 Evolution of Database Interaction in Java (🔧➡️⚙️➡️🚀) It’s fascinating how the “most natural way” to work with databases has evolved over time 👇 🔹 JDBC You write everything manually—queries, connections, result parsing. Full control, but a lot of boilerplate. 🔹 Hibernate ORM We move to object mapping. Less SQL, more Java objects. Cleaner, but still requires configuration and understanding ORM behavior. 🔹 Spring Boot with JPA Things get easier. Auto-configuration, reduced setup, better integration. Focus shifts more toward business logic. 🔹 Spring Data JPA (Repository methods) 🤯 Now this feels like magic! Define methods → Framework generates queries → Minimal SQL needed. 👉 From writing complex SQL to just defining method names… we’ve come a long way. 💡 But here’s the reality: Every layer matters. Understanding JDBC and SQL makes you a stronger developer—even when using high-level abstractions. 📌 Abstraction reduces effort, but fundamentals build mastery. What’s your preferred way of interacting with databases? 👇 #Java #SpringBoot #JPA #Hibernate #BackendDevelopment #SoftwareEngineering #LearningJourney
To view or add a comment, sign in
-
-
Day 29. I enabled query logging for the first time. What I saw surprised me. I had this: List<User> users = userRepository.findAll(); Simple. Clean. One line. I assumed Hibernate was handling it efficiently. Then I added this: spring.jpa.show-sql=true And watched the logs. Not one query. Multiple hidden ones. → Fetch users → Then fetch relationships → Then more queries behind the scenes Hibernate wasn’t optimizing anything. It was executing exactly what I told it to — lazily, one relationship at a time. That’s when it clicked. ORM doesn’t optimize your queries. It executes what you tell it to. (see fix below 👇) Or better: 👉 Fetch only what you need (DTO projection) What I learned: → Hibernate is not magic → Default behavior is not always efficient → You are responsible for query performance The hard truth: → “It works” doesn’t mean “it’s optimized” → ORM hides complexity — it doesn’t remove it Writing code that runs is easy. Understanding what your database is actually doing is what makes you a backend developer. Have you ever checked your query logs and been surprised? 👇 Drop your experience #SpringBoot #Java #Hibernate #BackendDevelopment #Performance #JavaDeveloper
To view or add a comment, sign in
-
-
Was working on a Spring Boot REST API the other day. Everything looked fine entity class, repository, controller all set up. But two fields, createdAt and updatedAt, were coming back null in every response. Spent time checking the constructor, the DTO mapping, the database config. Turns out I just needed two annotations: @CreationTimestamp → auto-sets the time when record is created @UpdateTimestamp → auto-updates the time on every save Two lines. That's it. Hibernate handles everything behind the scenes you don't set these manually. One more thing I'd missed without @JsonFormat, Java's Timestamp serializes weirdly in JSON. Add this and it formats cleanly: @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") Small things. But they'll silently break your API if you miss them. #Java #SpringBoot #Backend #LearningInPublic #HibernateJPA
To view or add a comment, sign in
-
-
Most slow Spring Boot apps have the same problem. It's not the database. It's how you ask it. After years debugging production performance, I see the same pattern over and over: The N+1 query problem. You fetch a list of orders. Looks fine. Then you loop through them and access order.getCustomer(). Hibernate happily fires one extra query per order. 100 orders → 101 queries. Your endpoint goes from 50ms to 4 seconds. Your database CPU climbs. Nobody understands why. The trap is that the code looks clean. Lazy loading is doing exactly what it said it would. The framework isn't broken. The mental model is. What actually fixes it: - JOIN FETCH for predictable, single-use queries - @EntityGraph for reusable fetch plans - Batch fetching (hibernate.default_batch_fetch_size) when you need lazy but not crazy - DTO projections when you don't need the full entity at all ⚠️ The mistake most teams make is enabling spring.jpa.show-sql=true, seeing 5 queries per request, and shrugging. Five becomes fifty under load. Fifty becomes the incident. The fix is not "tune the database." The fix is reading what your ORM is actually doing. Hibernate is not slow. Most Hibernate code is. What's the worst N+1 you've seen in production? #Java #SpringBoot #Backend #Hibernate #Performance #SoftwareEngineering
To view or add a comment, sign in
-
-
Yesterday we talked about how Lombok can crash your app through Hibernate relationships. Today, let’s talk about the other silent killer in those exact same entities: The N+1 Query Problem. If you work with Spring Data JPA long enough, you will eventually write a piece of code that looks completely fine, passes all unit tests, and then completely chokes your database in production. Usually, the culprit is the N+1 problem. The Trap: Let’s say you have a User entity with a @OneToMany relationship to Order. You want to fetch 100 users and print their order totals. You write a simple repository call: userRepository.findAll() (This is 1 query).Then, you loop through those 100 users and call user.getOrders(). What you think happens: Hibernate fetches the users and their orders efficiently. What actually happens: Because relationships are (and should be!) FetchType.LAZY by default, Hibernate executes 1 query to get the 100 users, and then 100 additional queries—one for each user—to fetch their orders. You just hit your database 101 times for a single API request. Multiply that by 1,000 concurrent users, and your DBA is calling you in a panic. 😅 The Senior Fixes: 1. The Quick Fix: JOIN FETCH Instead of using standard findAll(), write a custom JPQL query: SELECT u FROM User u JOIN FETCH u.orders This forces Hibernate to grab everything in a single, efficient JOIN query. 2. The Elegant Fix: @EntityGraph If you don't want to write custom queries, Spring Boot lets you use Entity Graphs to dynamically define which lazy associations should be fetched eagerly for a specific method call. 3. The Ultimate Fix: DTO Projections Stop fetching full Managed Entities if you just need to read data! Write a query that maps directly to a Record or DTO. It bypasses the Hibernate proxy lifecycle entirely and is blazing fast. JPA makes the easy things trivial, but it makes the hard things incredibly dangerous if you don't look at the generated SQL. What is your team's standard way of catching N+1 queries before they hit production? Do you use a specific tool, or rely on code reviews? 👇 #Java #SpringBoot #Hibernate #BackendDevelopment #Microservices #SoftwareEngineering #PerformanceTuning #CleanCode
To view or add a comment, sign in
-
🚨 𝗧𝗵𝗲 𝗦𝗶𝗹𝗲𝗻𝘁 𝗣𝗲𝗿𝗳𝗼𝗿𝗺𝗮𝗻𝗰𝗲 𝗞𝗶𝗹𝗹𝗲𝗿 𝗶𝗻 𝗦𝗽𝗿𝗶𝗻𝗴 𝗕𝗼𝗼𝘁 — 𝗡+𝟭 𝗤𝘂𝗲𝗿𝘆 𝗣𝗿𝗼𝗯𝗹𝗲𝗺 Everything works fine… Until your API suddenly becomes slow in production 😅 That’s when many discover the N+1 Query Problem. 𝗪𝗵𝗮𝘁 𝗶𝘀 𝗡+𝟭? You fetch 1 parent entity Then Hibernate runs N additional queries for child entities 👉 Total queries = N + 1 👉 Performance = 📉 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 Fetching Departments with Employees: Instead of 1 query, Hibernate runs: • 𝟭 𝗾𝘂𝗲𝗿𝘆 → Fetch departments • 𝗡 𝗾𝘂𝗲𝗿𝗶𝗲𝘀 → Fetch employees for each department Boom 💥 Performance issue. 𝗖𝗼𝗺𝗺𝗼𝗻 𝗠𝗶𝘀𝘁𝗮𝗸𝗲 A common instinct is to switch to EAGER loading to fix it. But… ❌ EAGER can also cause N+1 ❌ More memory usage ❌ Less control 𝗕𝗲𝘁𝘁𝗲𝗿 𝗦𝗼𝗹𝘂𝘁𝗶𝗼𝗻𝘀 ✅ ✔️ JOIN FETCH Fetch everything in a single query ✔️ EntityGraph Cleaner and more flexible approach ✔️ Batch Size Reduces queries for large datasets ✔️ DTO Projection Best for read-only APIs and performance Understanding this early can save hours of debugging in production 🚀 #connections #SpringBoot #Hibernate #Java #Backend #Performance #JPA #SoftwareEngineering #interviewPrep #interviewQuestion
To view or add a comment, sign in
-
-
If you’ve ever wrestled with string-based queries or struggled to keep your database layer type-safe, jOOQ might be the missing piece in your Java stack. N47 Igor Stojanoski #jooq #sql #java 👍 😊 https://lnkd.in/dDYEck3V
To view or add a comment, sign in
-
HIBERNATE'S HIDDEN TRAP: EAGER VS LAZY LOADING (PART 1) 🛌⚡ In Hibernate, there are two ways to load data, and both of them can kill your performance if you aren't careful. 1. THE EAGER TRAP 🍔 Eager loading is "greedy." It fetches the parent and all related children in one go. Sounds efficient? Until you realize that fetching one User just triggered a massive chain reaction that loaded 500 Orders, 2000 Items, and 5000 Reviews into memory. Your "simple" query just became a memory bomb. 2. THE LAZY TRAP 💤 Lazy loading is "procrastinating." it only fetches the children when you actually ask for them. But if you ask for them inside a loop, you trigger the N+1 problem. One query to fetch the list, and then 100 more queries to fetch the details. 3. THE "DETACHED ENTITY" CRASH 💥 We’ve all seen the LazyInitializationException. This happens when you try to access lazy data after the DB transaction has closed. It’s Hibernate’s way of saying, "You should have asked for this earlier!" In Part 2, I’ll show you how to use Join Fetching to get the best of both worlds. #Hibernate #Java #ORM #DatabasePerformance #BackendDevelopment #SoftwareEngineering #JavaPersistenceAPI #DatabaseDesign #PerformanceTuning #TechTips #CodingLife #SoftwareArchitecture #API #SQL
To view or add a comment, sign in
-
-
I wrote "Clean Code" that silently killed our database performance. 📉🐢 It worked perfectly on my local machine. The tests passed. The UI was fast. But as soon as we hit 50 concurrent users in staging, the database CPU spiked to 90%. The Mistake: I was fetching a list of Orders, and for each order, I was calling order.getUser().getName(). In my head, it was one simple query. In reality, Spring was executing: 👉 1 query to get all Orders. 👉 100 extra queries to get each User. This is the classic N+1 Problem, and in a microservices environment, it’s a death sentence for your latency. The 2026 Solution (My Fix): Instead of a basic.findAll(), I moved to a custom @Query with a JOIN FETCH. java @Query("SELECT o FROM Order o JOIN FETCH o.user") List<Order> findAllWithUsers(); Use the code with caution. The Result? One single, efficient query. Database CPU dropped back to 10%. API response time cut by 70%. Lesson Learned: Don't just trust the "magic" of JPA. Always check your logs to see what Hibernate is actually doing behind the scenes. Have you ever been surprised by a "hidden" query? What’s your go-to tool for monitoring SQL in Spring Boot? 👇 #SpringBoot #JavaDeveloper #BackendEngineering #PerformanceOptimization #LearningInPublic #AhmedabadTech #CleanCode #Database
To view or add a comment, sign in
Explore content categories
- Career
- Productivity
- Finance
- Soft Skills & Emotional Intelligence
- Project Management
- Education
- Technology
- Leadership
- Ecommerce
- User Experience
- Recruitment & HR
- Customer Experience
- Real Estate
- Marketing
- Sales
- Retail & Merchandising
- Science
- Supply Chain Management
- Future Of Work
- Consulting
- Writing
- Economics
- Artificial Intelligence
- Employee Experience
- Workplace Trends
- Fundraising
- Networking
- Corporate Social Responsibility
- Negotiation
- Communication
- Engineering
- Hospitality & Tourism
- Business Strategy
- Change Management
- Organizational Culture
- Design
- Innovation
- Event Planning
- Training & Development
N+1 is the silent killer in most Spring Boot apps. one trick that helps is enabling hibernate.show_sql and hibernate.format_sql in dev profile so you can see exactly how many queries are firing. @EntityGraph is cleaner than JOIN FETCH for most cases since you can define it declaratively on the repository method