Careeroza
Real World Example
Production Incident Review
A ShopEase sale day incident, written the way teams at Flipkart or PhonePe write post-mortems. Each root cause maps to a pitfall from this module:
| Symptom | Root cause | Fix shipped |
|---|---|---|
| HikariCP "Connection is not available, request timed out after 30000ms" | OSIV held connections during slow JSON writes to mobile clients | open-in-view=false, DTOs built in the service |
GET /api/users/{id} p99 = 9 s for resellers | user.getOrders() loaded 300k orders to show a count | removed the collection; countByUserId query |
Cart items vanished randomly from Set | Lombok @Data hashCode used generated id | proxy-safe equals/hashCode |
| StackOverflowError in error logs | logging log.info("order {}", order) with generated toString | hand-written toString |
The Fixed Endpoint
public record UserSummary(Long id, String name, String email, long orderCount, BigDecimal lifetimeValue) {}
public interface UserRepository extends JpaRepository<User, Long> {
@Query("""
select new com.shopease.user.UserSummary(
u.id, u.name, u.email, count(o.id), coalesce(sum(o.total), 0))
from User u left join Order o on o.user = u
where u.id = :id
group by u.id, u.name, u.email
""")
Optional<UserSummary> findSummary(@Param("id") Long id);
}
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
class UserController {
private final UserService users;
@GetMapping("/{id}")
UserSummary get(@PathVariable Long id) {
return users.summary(id); // DTO only; no entity leaves the service
}
}Request
curl localhost:8080/api/users/42Example output
select u1_0.id,u1_0.name,u1_0.email,count(o1_0.id),coalesce(sum(o1_0.total),0)
from users u1_0 left join orders o1_0 on o1_0.user_id=u1_0.id
where u1_0.id=? group by u1_0.id,u1_0.name,u1_0.email
HTTP/1.1 200 OK
{"id":42,"name":"Kiran Traders","email":"[email protected]","orderCount":312904,"lifetimeValue":48211930.50}Exercise: After turning OSIV off, GET /api/orders/7 throws LazyInitializationException: could not initialize proxy [Product#3] - no Session. The controller returns the Order entity. Give two correct fixes.
Show answer
1. Return a DTO built inside the @Transactional(readOnly = true) service method,
loading what it needs in one query:
select o from Order o join fetch o.items i join fetch i.product where o.id = :id
then map to OrderDto(id, status, items[name, qty, price]).
2. Use a DTO projection query directly (select new ...OrderLine(...)) and group the lines.
(Wrong fix: turning OSIV back on or making associations EAGER.)