Học viên: Vũ Bá Phúc
Lớp: Java Fullstack 15
Email: phucnhan20022022@gmail.com
Số điện thoại: 0968616076
Nguồn tham khảo : https://www.baeldung.com

1. Giới Thiệu

Hướng dẫn này sẽ thảo luận về cách cấu hình Spring Transactions đúng cách, cách sử dụng chú thích @Transactional và các điểm nguy hiểm tiềm ẩn phổ biến.

Để tìm hiểu kỹ hơn về cấu hình core persistence, hãy xem hướng dẫn Spring với JPA.

Về cơ bản, có hai cách riêng biệt để cấu hình Transactions, chú thích và AOP, mỗi cách có điểm ưu việt riêng. Chúng ta sẽ thảo luận về cấu hình chú thích phổ biến hơn ở đây.

2. Configure Transactions

Spring 3.1 giới thiệu chú thích @EnableTransactionManagement mà chúng ta có thể sử dụng trong lớp @Configuration để kích hoạt hỗ trợ Transactions:

@Configuration
@EnableTransactionManagement
public class PersistenceJPAConfig{

   @Bean
   public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
       //...
   }

   @Bean
   public PlatformTransactionManager transactionManager() {
      JpaTransactionManager transactionManager = new JpaTransactionManager();
      transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
      return transactionManager;
   }
}

Tuy nhiên, nếu chúng ta đang sử dụng dự án Spring Boot và có phụ thuộc spring-data hoặc spring-tx trên đường dẫn lớp, thì quản lý giao dịch sẽ được bật theo mặc định .

3. Configure Transactions với XML

Đối với các phiên bản trước 3.1 hoặc nếu Java không phải là một tùy chọn, đây là cấu hình XML sử dụng annotation-drivennamespace:

<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
   <property name="entityManagerFactory" ref="myEmf" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />

4. Chú thích @Transactional

Với các Transactions được cấu hình, giờ đây chúng ta có thể chú thích một bean bằng @Transactional ở cấp lớp hoặc cấp phương thức:

@Service
@Transactional
public class FooService {
    //...
}

Chúng ta có thể tiếp tục cấu hình cho annotation với:

  • Propagation Type của giao dịch
  • Isolation Level của giao dịch
  • Timeout cho hoạt động được bao bọc bởi giao dịch
  • readOnly flag – gợi ý cho nhà cung cấp lưu trữ rằng giao dịch nên chỉ đọc
  • Quy tắc Rollback cho giao dịch

Lưu ý rằng theo mặc định, rollback xảy ra chỉ cho các exception chạy trong thời gian thực, không được kiểm tra. Các exception kiểm tra không kích hoạt việc rollback của giao dịch. Tất nhiên, chúng ta có thể cấu hình hành vi này với các tham số annotation rollbackFornoRollbackFor.

5. Những điểm tiềm ẩn

5.1. Transactions và Proxy

Tổng quan là Spring tạo ra các proxy cho tất cả các class được đánh dấu bằng @Transactional, cả trên class hoặc bất kỳ method nào. Proxy cho phép framework tiêm logic transaction trước và sau khi method được chạy, chủ yếu để bắt đầu và commit transaction.

Điều quan trọng cần lưu ý là, nếu bean transactional đang thực hiện một interface, mặc định proxy sẽ là Java Dynamic Proxy. Điều này có nghĩa là chỉ có các cuộc gọi phương thức bên ngoài đến thông qua proxy mới được chặn lại. Bất kỳ cuộc gọi tự gọi nào cũng sẽ không bắt đầu bất kỳ transaction nào, ngay cả khi phương thức có đánh dấu @Transactional.

Một lưu ý khác khi sử dụng proxy là chỉ có các method public mới được đánh dấu bằng @Transactional. Các method có bất kỳ quyền truy cập nào khác sẽ đơn giản là bỏ qua đánh dấu một cách im lặng vì chúng không được tạo proxy.

5.2. Cập nhật cấp độ cô lập

courseDao.createWithRuntimeException(course);

Chúng ta cũng có thể thay đổi cấp độ cô lập của transaction:

@Transactional(isolation = Isolation.SERIALIZABLE)

Lưu ý rằng điều này thực sự đã được giới thiệu trong Spring 4.1; nếu chúng ta chạy ví dụ trên trước Spring 4.1, kết quả sẽ là:

org.springframework.transaction.InvalidIsolationLevelException: Standard JPA does not support custom isolation levels – use a special JpaDialect for your JPA implementation

5.3. Read-Only Transactions

readOnly flag thường gây ra sự nhầm lẫn, đặc biệt là khi làm việc với JPA. Theo Javadoc:

This just serves as a hint for the actual transaction subsystem; it will not necessarily cause failure of write access attempts. A transaction manager which cannot interpret the read-only hint will not throw an exception when asked for a read-only transaction.

Thực tế là chúng ta không thể chắc chắn rằng một thao tác insert hoặc update không xảy ra khi readOnly flag được thiết lập. Hành vi này phụ thuộc vào nhà cung cấp, trong khi JPA thì không phụ thuộc vào nhà cung cấp.

Điều quan trọng cần hiểu là readOnly flag chỉ có tác dụng trong phạm vi của một transaction. Nếu một hoạt động xảy ra bên ngoài một ngữ cảnh transactional, flag sẽ được bỏ qua. Một ví dụ đơn giản cho điều đó là gọi một phương thức được chú thích bằng:

@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)

Trong một ngữ cảnh không transactional, transaction sẽ không được tạo ra và readOnly flag sẽ được bỏ qua.

5.4. Nhật ký giao dịch

Chúng ta có thể tinh chỉnh cấu hình logging trong các package liên quan đến transactional. Package phù hợp trong Spring là org.springframework.transaction, cần được cấu hình với mức độ logging là TRACE. Việc này giúp chúng ta có thể theo dõi các hoạt động của transaction trong quá trình thực thi ứng dụng và dễ dàng phát hiện và giải quyết các vấn đề liên quan.

5.5. Transaction Rollback

Chú thích @Transactional là dữ liệu siêu dùng để chỉ định ngữ nghĩa của các giao dịch trong một phương thức. Chúng ta có hai cách để rollback một giao dịch: cách khai báo và cách lập trình.

Trong cách tiếp cận khai báo, chúng ta chú thích các phương thức bằng chú thích @Transactional. Chú thích @Transactional sử dụng các thuộc tính rollbackFor hoặc rollbackForClassName để rollback các giao dịch và các thuộc tính noRollbackFor hoặc noRollbackForClassName để tránh rollback trên các ngoại lệ được liệt kê.

Hành vi rollback mặc định trong cách tiếp cận khai báo sẽ rollback trên các ngoại lệ thời gian chạy.

Hãy xem một ví dụ đơn giản sử dụng cách tiếp cận khai báo để rollback một giao dịch cho các ngoại lệ thời gian chạy hoặc lỗi:

@Transactional
public void createCourseDeclarativeWithRuntimeException(Course course) {
    courseDao.create(course);
    throw new DataIntegrityViolationException("Throwing exception for demoing Rollback!!!");
}

Tiếp theo, chúng ta sẽ sử dụng phương pháp khai báo để khôi phục giao dịch cho các ngoại lệ được kiểm tra được liệt kê. Rollback trong ví dụ của chúng tôi là trên SQLException :

@Transactional(rollbackFor = { SQLException.class })
public void createCourseDeclarativeWithCheckedException(Course course) throws SQLException {
    courseDao.create(course);
    throw new SQLException("Throwing exception for demoing rollback");
}

Chúng ta hãy xem cách sử dụng đơn giản thuộc tính noRollbackFor trong cách tiếp cận khai báo để ngăn việc khôi phục giao dịch đối với ngoại lệ được liệt kê:

@Transactional(noRollbackFor = { SQLException.class })
public void createCourseDeclarativeWithNoRollBack(Course course) throws SQLException {
    courseDao.create(course);
    throw new SQLException("Throwing exception for demoing rollback");
}

Theo cách tiếp cận có lập trình , chúng tôi khôi phục các giao dịch bằng cách sử dụng TransactionAspectSupport :

public void createCourseDefaultRatingProgramatic(Course course) {
    try {
       courseDao.create(course);
    } catch (Exception e) {
       TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}

Chiến lược rollback khai báo nên được ưa chuộng hơn chiến lược rollback có lập trình .

6. Kết luận

Trong bài viết này, chúng ta đã tìm hiểu về cấu hình cơ bản của các thuộc tính giao dịch sử dụng cả Java và XML. Chúng ta cũng đã học cách sử dụng @Transactional và các best practice trong Transactional Strategy.