Careeroza
Real World Example
Amazon-style "Your Orders" Page
The "Your Orders" page of a large store shows, per order: date, status, total, and the first product names with thumbnails. A real team found this page taking 1.8 s for heavy users. The log showed 1 + 40 + 120 queries: orders, then items per order, then the product per item. Here is how ShopEase fixes it with a paged order query plus batch fetching (fetch joins cannot be combined safely with paging a collection; see Pagination and Sorting).
public interface OrderRepository extends JpaRepository<Order, Long> {
Page<Order> findByUserId(Long userId, Pageable pageable); // 1 query + 1 count
}
@Entity @Table(name = "orders")
public class Order {
@OneToMany(mappedBy = "order")
@BatchSize(size = 50)
private List<OrderItem> items = new ArrayList<>();
}
@Entity @Table(name = "order_items")
public class OrderItem {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "product_id")
private Product product; // batch-loaded via default_batch_fetch_size
}Before and After
Example
hibernate.generate_statistics=true prints session metrics at the end of the request:
GET /api/me/orders?page=0&size=40Example output
-- before (all lazy, no batching)
StatisticalLoggingSessionEventListener : Session Metrics {
161 JDBC statements executed; 1734 ms total
}
-- after (default_batch_fetch_size=50 + @BatchSize on items)
DEBUG org.hibernate.SQL : select o1_0.id,o1_0.status,o1_0.total,o1_0.user_id from orders o1_0 where o1_0.user_id=? order by o1_0.created_at desc limit ?,?
DEBUG org.hibernate.SQL : select count(o1_0.id) from orders o1_0 where o1_0.user_id=?
DEBUG org.hibernate.SQL : select i1_0.order_id,i1_0.id,i1_0.price,i1_0.product_id,i1_0.quantity from order_items i1_0 where i1_0.order_id in (?,?,?,...)
DEBUG org.hibernate.SQL : select p1_0.id,p1_0.category_id,p1_0.description,p1_0.name,p1_0.price,p1_0.stock from products p1_0 where p1_0.id in (?,?,?,...)
StatisticalLoggingSessionEventListener : Session Metrics {
5 JDBC statements executed; 38 ms total
}Team Checklist
- All associations LAZY;
spring.jpa.open-in-view=false. hibernate.default_batch_fetch_sizeset (16–100) as a global safety net.- Each screen has one repository method that fetches exactly its data (fetch join, entity graph or DTO).
- SQL logging on in dev; query-count assertions in integration tests for the busiest endpoints.