readOnly = true là hint cho transaction — không phải enforcement cứng.
@Transactional(readOnly = true)
public List<User> findAll() { return userRepo.findAll(); }Tác dụng:
1. Hibernate tắt dirty checking — không snapshot entity, không track thay đổi → giảm memory + CPU.
2. Flush mode = MANUAL — không flush session tự động.
3. DB có thể optimize — route sang read replica, dùng read-only transaction (Postgres: SET TRANSACTION READ ONLY).
Lưu ý:
- Không chặn write — Spring không enforce, chỉ hint. Code vẫn entity.setX() được, chỉ là Hibernate không persist (không flush).
- Một số DB/JDBC driver ignore hint nếu không support.
Best practice: đặt @Transactional(readOnly = true) ở class level, override @Transactional (writable) ở method nào ghi:
@Service
@Transactional(readOnly = true) // default cho mọi method
class UserService {
public User findById(Long id) { ... }
@Transactional // override cho write
public User create(User u) { ... }
}readOnly = true is a hint for the transaction — not hard enforcement.
@Transactional(readOnly = true)
public List<User> findAll() { return userRepo.findAll(); }Effects:
1. Hibernate disables dirty checking — does not snapshot entities or track changes → less memory + CPU.
2. Flush mode = MANUAL — sessions are not flushed automatically.
3. DB may optimise — routing to a read replica, using a read-only transaction (Postgres: SET TRANSACTION READ ONLY).
Caveats:
- Does not block writes — Spring does not enforce, it is just a hint. Code can still call entity.setX(); Hibernate just will not persist (no flush).
- Some DBs/JDBC drivers ignore the hint if unsupported.
Best practice: put @Transactional(readOnly = true) at class level, override with writable @Transactional on write methods:
@Service
@Transactional(readOnly = true) // default for all methods
class UserService {
public User findById(Long id) { ... }
@Transactional // override for writes
public User create(User u) { ... }
}