[Java Backend Zero to Hello] BÀI 4.10: TRANSACTION & LOCKING
📚 Bài viết thuộc series Java Backend Zero to Hello 📌 Phần: Phase 4: Database & SQL | Bài 45/86
BÀI 4.10: TRANSACTION & LOCKING
Mục tiêu
- Hiểu Transaction và ACID
- Sử dụng @Transactional
- Các Isolation Level
- Optimistic và Pessimistic Locking
1. TRANSACTION LÀ GÌ?
Transaction là một đơn vị công việc logic, tuân thủ ACID:
- Atomicity: Tất cả hoặc không có gì
- Consistency: Dữ liệu luôn hợp lệ
- Isolation: Các transaction độc lập
- Durability: Commit thì không mất
2. @TRANSACTIONAL (SPRING)
2.1 Cơ bản
@Service
public class UserService {
@Transactional
public void createUser(User user) {
userRepository.save(user);
// Tự động commit nếu không có exception
// Tự động rollback nếu có RuntimeException
}
}
2.2 Các thuộc tính
@Transactional(
propagation = Propagation.REQUIRED, // Mặc định
isolation = Isolation.DEFAULT,
timeout = 30, // seconds
readOnly = false,
rollbackFor = Exception.class, // Rollback cho cả checked
noRollbackFor = RuntimeException.class // Không rollback
)
2.3 Propagation
| Propagation | Mô tả |
|---|---|
REQUIRED |
Dùng transaction hiện có, hoặc tạo mới (mặc định) |
REQUIRES_NEW |
Luôn tạo transaction mới |
NESTED |
Nested transaction |
SUPPORTS |
Dùng transaction nếu có |
NOT_SUPPORTED |
Chạy không có transaction |
MANDATORY |
Bắt buộc có transaction |
NEVER |
Không được có transaction |
2.4 Ví dụ
@Service
public class OrderService {
@Transactional
public void createOrder(Order order) {
orderRepository.save(order);
// Gọi method khác - cùng transaction
paymentService.processPayment(order);
}
}
@Service
public class PaymentService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void processPayment(Order order) {
// Transaction riêng - commit độc lập
paymentRepository.save(new Payment(order));
}
}
3. ISOLATION LEVEL
3.1 Các vấn đề
| Vấn đề | Mô tả |
|---|---|
| Dirty Read | Đọc dữ liệu chưa commit |
| Non-repeatable Read | Đọc 2 lần khác giá trị |
| Phantom Read | Số dòng thay đổi giữa 2 lần đọc |
3.2 Các level
| Level | Dirty Read | Non-repeatable | Phantom |
|---|---|---|---|
READ_UNCOMMITTED |
✅ | ✅ | ✅ |
READ_COMMITTED |
❌ | ✅ | ✅ |
REPEATABLE_READ |
❌ | ❌ | ✅ |
SERIALIZABLE |
❌ | ❌ | ❌ |
3.3 Cấu hình
@Transactional(isolation = Isolation.READ_COMMITTED)
public void processOrder() { ... }
4. LOCKING
4.1 Optimistic Locking
Giả định không có conflict, kiểm tra khi commit.
@Entity
public class Product {
@Id
@GeneratedValue
private Long id;
private String name;
private int stock;
@Version
private Long version; // Tự động tăng khi update
}
// Sử dụng
Product product = em.find(Product.class, 1L);
product.setStock(product.getStock() - 1);
em.merge(product); // Ném OptimisticLockException nếu version không khớp
4.2 Pessimistic Locking
Khóa dòng ngay khi đọc.
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT p FROM Product p WHERE p.id = :id")
Optional<Product> findByIdForUpdate(@Param("id") Long id);
@Transactional
public void decreaseStock(Long productId, int quantity) {
Product product = productRepository.findByIdForUpdate(productId)
.orElseThrow();
product.setStock(product.getStock() - quantity);
// Lock được giải phóng khi commit
}
4.3 So sánh
| Optimistic | Pessimistic |
|---|---|
| Không khóa khi đọc | Khóa ngay khi đọc |
| Kiểm tra khi commit | Chờ commit |
| Hiệu năng cao | Hiệu năng thấp |
| Ít conflict | Nhiều conflict |
| Dùng @Version | Dùng SELECT FOR UPDATE |
5. DEADLOCK
5.1 Ví dụ
Transaction A: Lock row 1, cần lock row 2
Transaction B: Lock row 2, cần lock row 1
→ Deadlock!
5.2 Cách tránh
- Lock theo thứ tự nhất quán
- Dùng timeout
- Retry khi gặp deadlock
- Tránh lock quá lâu
@Transactional
public void transfer(Long fromId, Long toId, double amount) {
// Lock theo thứ tự ID tăng dần
Long firstId = Math.min(fromId, toId);
Long secondId = Math.max(fromId, toId);
Account first = accountRepository.findByIdForUpdate(firstId);
Account second = accountRepository.findByIdForUpdate(secondId);
// ...
}
6. TRANSACTION TEMPLATE
@Service
public class UserService {
@Autowired
private TransactionTemplate transactionTemplate;
public void createUser(User user) {
transactionTemplate.execute(status -> {
userRepository.save(user);
return null;
});
}
}
7. BÀI TẬP THỰC HÀNH
Bài 1: Transfer Money
@Transactional(isolation = Isolation.READ_COMMITTED)
public void transfer(Long fromId, Long toId, double amount) {
Account from = accountRepository.findByIdForUpdate(fromId)
.orElseThrow(() -> new NotFoundException("From account"));
Account to = accountRepository.findByIdForUpdate(toId)
.orElseThrow(() -> new NotFoundException("To account"));
if (from.getBalance() < amount) {
throw new InsufficientFundsException();
}
from.setBalance(from.getBalance() - amount);
to.setBalance(to.getBalance() + amount);
}
Bài 2: Optimistic Locking
Thêm @Version vào entity, test conflict.
Bài 3: Rollback Rules
Test rollback với các loại exception khác nhau.
8. TÓM TẮT
| Khái niệm | Mô tả |
|---|---|
| @Transactional | Quản lý transaction |
| Propagation | Cách lan truyền transaction |
| Isolation | Mức độ cô lập |
| Optimistic Lock | Dùng @Version |
| Pessimistic Lock | SELECT FOR UPDATE |
| Deadlock | Xung đột khóa |
Hoàn thành Phase 4! Tiếp tục với Phase 5: Spring Framework & Spring Boot
🧭 Điều Hướng Series
⬅️ Bài trước: BÀI 4.9: QUERY NÂNG CAO TRONG JPA
📋 Lộ trình tổng quan: Xem Toàn Bộ Series
➡️ Bài tiếp theo: PHASE 5: SPRING FRAMEWORK & SPRING BOOT - Tổng Quan & Mục Tiêu
All rights reserved