The 1-minute fix that slashed my API response time by 40%.
We all know @Transactional makes database work easy. But are you using it correctly?
Here is a mistake I see in 8 out of 10 code reviews:
โ The Anti-Pattern:
@Service
public class OrderService {
@Transactional
public Order placeOrder(OrderDto dto) {
Order order = new Order();
// 1. Save the parent
orderRepository.save(order);
// 2. Loop and save children one by one
for (Item i : dto.getItems()) {
Item item = new Item();
item.setOrder(order);
itemRepository.save(item); // ๐ N+1 problem + multiple flush calls
}
return order;
}
}
The Problem: Even with @Transactional, calling .save() in a loop forces Hibernate to hit the database for every single item. Thatโs 50 round trips for 50 items.
โ
The Game-Changer:
Use Batch Insertion with a simple property change.
1.Add this to application.properties:
spring.jpa.properties.hibernate.jdbc.batch_size=50
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
2.Refactor your code to use .saveAll():
@Transactional
public Order placeOrder(OrderDto dto) {
Order order = new Order();
orderRepository.save(order); // Save parent first
List<Item> items = dto.getItems().stream()
.map(i -> new Item(i, order))
.collect(Collectors.toList());
itemRepository.saveAll(items); // ONE single INSERT statement for all items!
return order;
}
The Result:
- Before: 51 SQL queries for 50 items.
- After: 2 SQL queries for 50 items.
- Performance: 40% faster response time.
- DB Load: Drastically reduced.
The takeaway: @Transactional manages the transaction, but it doesn't batch your queries. You have to explicitly tell Hibernate to batch them.
Discussion Question: What's the one Spring Boot property you can't live without? Drop it in the comments! ๐
Top comments (0)