Part 03 · Spring Framework & Spring Boot · 3.2A

12 tình huống AOP, Transaction và JPA

Trả lời bằng proxy call path, persistence-context state và commit boundary; không chỉ đọc tên annotation.


1. Method có @Transactional nhưng không thấy transaction?
Kiểm tra object có phải Spring bean, call có đi qua proxy, hay đang self-invocation, private/final method hoặc thread mới. Cách sửa thường là tách operation sang collaborator và viết integration test đi qua bean proxy thật.
2. saveAndFlush() phát INSERT rồi phía sau throw exception?
Flush chỉ phát SQL trong transaction; RuntimeException thoát qua proxy vẫn làm rollback. IDENTITY có thể buộc INSERT sớm để lấy ID nhưng vẫn chưa phải commit.
3. Method catch RuntimeException và trả FAILED, nhưng DB lại có dữ liệu?
Exception bị nuốt nên proxy thấy return bình thường và cố commit. Rethrow hoặc đánh dấu rollback-only; response cũng phải biểu diễn operation đã thất bại, không tạo false success.
4. Method ném IOException nhưng DB vẫn commit?
Checked exception mặc định không rollback. Dùng rollbackFor nếu business atomicity yêu cầu và test cả trường hợp có/không rule.
5. Inner REQUIRED fail, outer catch rồi nhận UnexpectedRollbackException?
Inner logical scope đã đánh physical transaction rollback-only; outer không thể commit dù đã catch. Không giả recovery trong cùng transaction; đổi boundary hoặc propagation theo invariant thực.
6. Audit REQUIRES_NEW không chạy riêng khi gọi this.audit()?
Self-invocation bỏ qua proxy. Tách audit sang bean khác. Đồng thời nhớ REQUIRES_NEW cần connection thứ hai và có nguy cơ pool starvation.
7. @Modifying update status nhưng entity vừa load vẫn có status cũ?
Bulk DML bypass persistence context. Clear, refresh hoặc tách transaction; trước khi clear phải cân nhắc pending dirty changes chưa flush.
8. Bulk update không tăng @Version, concurrent update bị ghi đè?
Bulk query bypass optimistic-lock machinery trừ khi query tự kiểm tra và tăng version. Đưa expected version vào predicate, increment version và assert affected-row count.
9. Custom @AuditedAction chạy hai lần hoặc business method chạy hai lần?
@Around có thể gọi proceed() sai số lần hoặc nhiều pointcut/aspect overlap. Advice phải proceed đúng một lần; inspect và test advisor chain.
10. Aspect ghi SUCCESS nhưng transaction commit sau đó fail constraint?
Advice thấy target return chưa đồng nghĩa DB commit đã thành công. Audit committed outcome cần transaction synchronization hoặc Outbox và phải đặt đúng thứ tự với transaction interceptor.
11. Native PostgreSQL query nhanh nhưng pagination trả sai total?
Native count query có thể không tương ứng filter, joins hoặc deduplication của data query. Viết count query đúng, kiểm execution plan và test dữ liệu duplicate/null.
12. Transactional test pass nhưng production after-commit listener fail?
Test-managed transaction thường rollback và không mô phỏng commit visibility hoặc after-commit phase. Chủ động commit rồi quan sát từ transaction hoặc thread khác; test failure/retry của listener riêng.

Rubric