Transaction management với @EnableTransactionManagement trong ứng dụng Spring

Trong bài viết JDBC transaction management trong Spring, mình đã giới thiệu với các bạn về cách cấu hình transaction management sử dụng XML file. Chúng ta cũng có thể sử dụng code Java để làm điều này với annotation @EnableTransactionManagement. Cụ thể như thế nào? Chúng ta sẽ cùng nhau tìm hiểu trong bài viết này các bạn nhé!

Đầu tiên, mình sẽ tạo mới một Maven project để làm ví dụ:

Chúng ta sẽ sử dụng Java 11 cho project này:

Spring Context và Spring JDBC như sau:

Mình sẽ sử dụng MySQL database để làm ví dụ:

Để so sánh cách cấu hình với tập tin XML, trong bài viết này, mình cũng tạo một ứng dụng ngân hàng nhỏ cho phép chúng ta có thể chuyển tiền từ tài khoản A sang tài khoản B. Quá trình chuyển khoản này sẽ gồm 2 bước: trừ tiền trong tài khoản A và cộng tiền vào tài khoản B. Bất kỳ exception nào xảy ra trong quá trình chuyển tiền thì tất cả các hoạt động sẽ được rollback lại. Tương tự như bài viết trước.

Mình cũng định nghĩa một table với cấu trúc như sau:

Interface AccountDAO và implementation của nó AccountDAOImpl để làm việc với table account, interface AccountService và implementation của nó AccountServiceImpl để làm việc với business của ứng dụng, có nội dung như sau:

Như các bạn thấy, nếu sau khi trừ tiền trong tài khoản A, nếu số tiền còn lại của tài khoản A nhỏ hơn 80$, mình sẽ không cho chuyển nữa và throw Exception.

Bây giờ, mình sẽ tạo mới một class để cấu hình cho ứng dụng:

Trong class ApplicationConfiguration này, mình sử dụng annotation @ComponentScan để Spring scan và khởi tạo bean cho tất cả các class làm việc với table account mà mình đã tạo ở trên. Mình cũng đã khởi tạo bean cho đối tượng DataSource và JdbcTemplate để những class này sử dụng.

Ví dụ bây giờ trong database của mình có 2 tài khoản A và B với các thông tin như sau:

Giờ mình sẽ viết code để thực hiện việc chuyển 20$ từ tài khoản A sang tài khoản B. Nội dung của class Application sẽ như sau:

Bây giờ, nếu mình chạy ứng dụng này, các bạn sẽ thấy có lỗi xảy ra:

tài khoản A bị trừ tiền nhưng tài khoản B lại không nhận được tiền:

Để enable transaction management cho ứng dụng này với annotation @EnableTransactionManagement, các bạn hãy khai báo annotation @EnableTransactionManagement và một bean của TransactionManager trong class cấu hình của ứng dụng:

và annotate method transfer() của class AccountServiceImpl annotation @Transactional như trong bài viết trước:

Nói về annotation @EnableTransactionManagement thì là nó tương đương với khai báo <tx:annotation-driven/> mà mình đã đề cập trong bài viết trước đó các bạn! Annotation này có các attribute định nghĩa cách thức mà ứng dụng chúng ta sẽ quản lý transaction.

Attribute adviceMode định nghĩa cách thức chúng ta apply lập trình hướng khía cạnh sử dụng Java proxy, thư viện CGLIB hay thư viện AspectJ. Mặc định thì sử dụng PROXY các bạn nhé!

Attribute proxyTargetClass chỉ sử dụng cho activeMode là PROXY và nếu giá trị của nó là true thì thư viện CGLIB sẽ được sử dụng. Ngược lại, nếu giá trị của thuộc tính này là false thì Java proxy sẽ được sử dụng.

Annotation @Transactional thì mình đã đề cập với các bạn trong bài viết trước. Việc khai báo của annotation @Transactional ở đâu, class level hay method level sẽ giúp Spring biết chúng ta cần quản lý transaction ở level nào.

Interface TransactionManager có 2 sub-interface là PlatformTransactionManager và ReactiveTransactionManager.

PlatformTransactionManager dùng để thao tác với database theo kiểu imperative, còn ReactiveTransactionManager thì dành cho kiểu reactive các bạn nhé! Các bạn có thể sử dụng các implementation của từng sub-interface này phù hợp cho ứng dụng của mình. Trong ví dụ của bài viết này, mình đang sử dụng PlatformTransactionManager với implementation là DataSourceTransactionManager đó các bạn!

Cập nhập lại database để số tiền của tài khoản A về 100$ rồi chạy lại ứng dụng, các bạn sẽ thấy có lỗi xảy ra nhưng tài khoản A không bị trừ tiền nữa!

Add Comment