Spring JDBC – Transaction trong Spring JDBC, Code ví dụ Spring JDBC Transaction
1. Transaction là gì?
Transaction là 1 giao dịch (hay còn gọi là 1 giao tác) bao gồm 1 loạt các hành động được phải được thực hiện thành công cùng nhau, nếu 1 hành động thất bại thì tất cả các hành động trong loạt hành động đó sẽ trở về trạng thái ban đầu.
(Xem lại: Transaction là gì? Ví dụ với JDBC Transaction)
2. Code ví dụ Spring JDBC Transaction
Ở ví dụ này mình sử dụng:
- MySQL làm cơ sở dữ liệu (Xem lại: Cài đặt và cấu hình MySQL)
- Eclipse
- Maven
Tạo database ‘demo-spring-jdbc-transaction’
CREATE SCHEMA `demo-spring-jdbc-transaction` ;
Tạo table ‘user_info’
CREATE TABLE `demo-spring-jdbc-transaction`.`user_info` ( `id` INT NOT NULL, `name` VARCHAR(45) NULL, `address` VARCHAR(255) NULL, PRIMARY KEY (`id`));
Code ví dụ:
Thư viện sử dụng:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>stackjava.com</groupId> <artifactId>SpringJDBC</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.45</version> </dependency> </dependencies> </project>
Thư viện spring-tx cung cấp khả năng quản lý transaction bằng annotation. (ví dụ bạn đánh dấu 1 method là @Transactional thì tức là method đó đã được đặt trong 1 transaction)
File Spring config:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd"> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/demo-spring-jdbc-transaction" /> <property name="username" value="root" /> <property name="password" value="admin1234" /> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- Enable Annotation based Declarative Transaction Management --> <tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager" /> <!-- Creating TransactionManager Bean, since JDBC we are creating of type DataSourceTransactionManager --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <bean id="userDAO" class="stackjava.com.springjdbc.dao.UserDAO"> <property name="jdbcTemplate" ref="jdbcTemplate" /> </bean> </beans>
- tx:annotation-driven được sử dụng để thông báo với spring context rằng chúng ta sẽ sử dụng annotation để quản lý transaction. Thuộc tính transaction-manager được sử dụng để cung cấp bean name của transaction
- Bean transactionManager được dùng để tạo bean transaction khi datasource được tạo (các query tới database thông qua datasource sẽ được quản lý bởi transactionManager)
File entity:
package stackjava.com.springjdbc.entities; public class User { private int id; private String name; private String address; public User() { } public User(int id, String name, String address) { this.id = id; this.name = name; this.address = address; } @Override public String toString() { return "User [id=" + id + ", name=" + name + ", address=" + address + "]"; } // getter - setter }
File DAO:
package stackjava.com.springjdbc.dao; import java.util.List; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.annotation.Transactional; import stackjava.com.springjdbc.entities.User; public class UserDAO { private JdbcTemplate jdbcTemplate; @Transactional public void insert(List<User> listUser) { String sql = "INSERT INTO user_info (id, name, address) VALUES (?, ?, ?);"; for (User user: listUser) { jdbcTemplate.update(sql, user.getId(), user.getName(), user.getAddress()); System.out.println("Inserted user: " + user.getId() +" - " +user.getName()); } } public void insertWithoutTransaction(List<User> listUser) { String sql = "INSERT INTO user_info (id, name, address) VALUES (?, ?, ?);"; for (User user: listUser) { jdbcTemplate.update(sql, user.getId(), user.getName(), user.getAddress()); System.out.println("Inserted user: " + user.getId() +" - " +user.getName()); } } public JdbcTemplate getJdbcTemplate() { return jdbcTemplate; } public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } }
- Biến jdbcTemplate truy vấn dữ liệu thông qua datasource nên có thể hiểu jdbcTemplate được quản lý bởi transactionManager
- Method insert được đánh dấu @Transactional (enable quản lý transaction) tức là method này sẽ nằm trong 1 transaction, khi method này xảy ra 1 lỗi thì tất cả những truy vấn trong method này đều bị rollback.
- Method insertWithoutTransaction không được đánh dấu @Transaction nên nếu có lỗi xảy ra thì những hành động truy vấn trước đó sẽ không bị rollback lại
Demo:
package stackjava.com.springjdbc.mainapp; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import stackjava.com.springjdbc.dao.UserDAO; import stackjava.com.springjdbc.entities.User; public class Demo { public static void main(String[] args) throws SQLException { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); UserDAO userDAO = (UserDAO) ctx.getBean("userDAO"); List<User> listUser = new ArrayList<User> (); listUser.add(new User(1, "Nicolas Cage", "USA")); listUser.add(new User(2, "Abramovic", "Russia")); listUser.add(new User(1, "Thor", "Asgard")); userDAO.insert(listUser); // userDAO.insertWithoutTransaction(listUser); ((ClassPathXmlApplicationContext) ctx).close(); System.out.println("Done!"); } }
Kết quả:
User thứ nhất và thứ hai insert bình thường nhưng user thứ ba insert thất bại ví nó có id trùng với user thứ nhất.
Do method insert của UserDAO được đánh dấu @Transactional nên cả 2 user đầu tiên sẽ bị rollback lại không được save vào database.
Bây giờ bạn dùng method insertWithoutTransaction thay vì method insert.
Kết quả:
Cũng xảy ra lỗi khi insert user thứ ba nhưng 2 user đầu không bị rollback lại:
Okay, Done!
Download code ví dụ trên tại đây
Spring JDBC – Transaction trong Spring JDBC, Code ví dụ Spring JDBC Transaction
References:
https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html