@Transactional — declarative transaction: Spring tự động begin → commit → rollback.
Không cần viết connection.commit() thủ công.
@Service
class OrderService {
@Transactional
public void placeOrder(Order order) {
orderRepo.save(order);
inventoryService.reserve(order); // nếu throw exception → rollback ALL
paymentService.charge(order);
}
}Attribute chính:
- propagation — REQUIRED (default, join hoặc tạo mới), REQUIRES_NEW (luôn tạo mới, suspend cái cũ), NESTED (savepoint).
- isolation — READ_COMMITTED (default), REPEATABLE_READ, SERIALIZABLE.
- readOnly = true — hint cho ORM tắt dirty checking, DB route sang replica.
- rollbackFor — chỉ định exception class nào trigger rollback.
- timeout — giây trước khi force rollback.
Default rollback rule:
- Rollback khi unchecked exception (RuntimeException, Error).
- KHÔNG rollback khi checked exception (IOException...) → muốn rollback phải khai báo rollbackFor = Exception.class.
Gotcha lớn nhất — proxy bypass: Spring tạo proxy bao quanh bean. Gọi method @Transactional từ bên trong cùng class sẽ bypass proxy → không có transaction:
class Service {
public void a() { b(); } // ❌ bypass proxy, b() không có transaction
@Transactional public void b() {}
}Fix: tách b() sang class khác, hoặc self-inject (AopContext.currentProxy()).
@Transactional — declarative transactions: Spring handles begin → commit → rollback automatically.
No manual connection.commit() calls.
@Service
class OrderService {
@Transactional
public void placeOrder(Order order) {
orderRepo.save(order);
inventoryService.reserve(order); // any exception → rollback EVERYTHING
paymentService.charge(order);
}
}Key attributes:
- propagation — REQUIRED (default, join or create), REQUIRES_NEW (always new, suspends current), NESTED (savepoint).
- isolation — READ_COMMITTED (default), REPEATABLE_READ, SERIALIZABLE.
- readOnly = true — hints the ORM to skip dirty checking; the DB may route to a read replica.
- rollbackFor — exception classes that trigger rollback.
- timeout — seconds before forced rollback.
Default rollback rule:
- Rollback on unchecked exceptions (RuntimeException, Error).
- Does NOT rollback on checked exceptions (IOException...) → use rollbackFor = Exception.class if needed.
Biggest gotcha — proxy bypass: Spring wraps the bean in a proxy. Calling a @Transactional method from inside the same class bypasses the proxy → no transaction:
class Service {
public void a() { b(); } // ❌ bypasses proxy, b() has no transaction
@Transactional public void b() {}
}Fix: extract b() into another class, or self-inject (AopContext.currentProxy()).