0

[Java Backend Zero to Hello] BÀI 4.10: TRANSACTION & LOCKING

Java Backend Zero to Hello

📚 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

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí