Reading Time: 9 minutes

Trình quản lý giao dịch (transaction manager) là một trong những tính năng quan trọng và được sử dụng rộng rãi nhất của Spring framework. Trong bất kỳ ứng dụng doanh nghiệp nào, quản lý transaction luôn là một tác vụ quan trọng.

Ta đã từng tìm hiểu cách sử dụng JDBC API để làm điều này. Spring có khả năng quản lý transaction mạnh mẽ, giúp lập trình viên tập trung hơn vào logic nghiệp vụ thay vì phải lo lắng về tính toàn vẹn của dữ liệu mỗi khi có sự cố hệ thống.

Quản lý transaction trong Spring với JDBC

Quản lý transaction trong Spring với JDBC

Một số lợi ích khi sử dụng khả năng quản lý transaction của Spring:

  1. Hỗ trợ quản lý transaction kiểu declarative (khai báo). Với mô hình này, Spring sử dụng AOP (Aspect-Oriented Programming, kiểu lập trình tách biệt các chức năng phụ trợ ra khỏi code chính) trên các phương thức có transaction để đảm bảo tính toàn vẹn dữ liệu. Đây là phương pháp được khuyến khích sử dụng và hoạt động hiệu quả trong hầu hết các trường hợp.
  2. Hỗ trợ hầu hết các API cho transaction như JDBC, Hibernate, JPA, JDO, JTA. Ta chỉ cần sử dụng class triển khai transaction manager phù hợp. Ví dụ, org.springframework.jdbc.datasource.DriverManagerDataSource cho quản lý transaction với JDBC và org.springframework.orm.hibernate3.HibernateTransactionManager nếu ta đang dùng Hibernate làm công cụ ORM.
  3. Hỗ trợ quản lý transaction bằng lập trình thông qua việc sử dụng TransactionTemplate hoặc một triển khai của PlatformTransactionManager.

Hầu hết các tính năng mà ta cần ở một transaction manager đều được hỗ trợ kiểu declarative, vì vậy chúng ta sẽ sử dụng cách tiếp cận này trong dự án ví dụ dưới đây.

Ví dụ quản lý transaction trong Spring với JDBC

Ta sẽ tạo một dự án Spring JDBC đơn giản, trong đó ta sẽ cập nhật nhiều bảng trong cùng một transaction. Transaction này chỉ nên được commit (thực thi) khi tất cả các câu lệnh JDBC thực thi thành công. Ngược lại, nó phải được rollback (hoàn trở lại) để tránh tình trạng dữ liệu không nhất quán.

Nếu đã biết cách quản lý transaction trong JDBC, bạn có thể cho rằng ta có thể dễ dàng làm điều này bằng cách đặt auto-commit (chế độ tự động commit) thành false cho kết nối, sau đó dựa vào kết quả của tất cả các câu lệnh để quyết định commit hay rollback. Tuy ta có thể làm vậy, cách này sẽ tạo ra rất nhiều code lặp lại chỉ để phục vụ việc quản lý transaction.

Các đoạn code tương tự cũng sẽ phải xuất hiện ở mọi nơi cần đến transaction, dẫn đến code bị liên kết (coupling) quá chặt, khó bảo trì. Phương pháp khai báo transaction của Spring giải quyết những vấn đề này bằng cách sử dụng lập trình kiểu AOP để đạt được coupling lỏng hơn và loại bỏ các code rập khuôn khỏi ứng dụng.

Hãy cùng xem Spring thực hiện điều đó như thế nào qua một ví dụ đơn giản sau đây. Đầu tiên, chúng ta sẽ phải thiết lập database.

Thiết lập database

Ta sẽ tạo hai bảng để sử dụng và cập nhật cả hai trong một transaction duy nhất.

CREATE TABLE `Customer` (
  `id` int(11) unsigned NOT NULL,
  `name` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `Address` (
  `id` int(11) unsigned NOT NULL,
  `address` varchar(20) DEFAULT NULL,
  `country` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Ta có thể định nghĩa quan hệ khóa ngoại (foreign-key) từ cột id của bảng Address tới cột id của bảng Customer. Nhưng để đơn giản, ta sẽ không định nghĩa bất kỳ ràng buộc nào.

Vậy là phần thiết lập database đã xong, giờ ta sẽ tạo một dự án Spring Maven đơn giản trong Spring Tool Suite. Cấu trúc dự án cuối cùng sẽ trông như hình bên dưới.

Cấu trúc ứng dụng quản lý transaction trong Spring với JDBC

Hãy cùng xem xét từng thành phần một, tất cả sẽ kết hợp để tạo nên một ví dụ quản lý transaction trong Spring với JDBC đơn giản.

Dependency trong Maven

Vì chúng ta sử dụng JDBC API, ta cần thêm dependency (thư viện phụ thuộc) spring-jdbc vào ứng dụng. Ta cũng cần driver để kết nối tới cơ sở dữ liệu MySQL, vì vậy ta sẽ thêm cả dependency mysql-connector-java.

Artifact (tạo phẩm) spring-tx cung cấp các dependency cho việc quản lý transaction. Thông thường, STS sẽ tự động thêm các dependency này. Nếu không có, bạn cần phải tự thêm thủ công vào.

Bạn có thể thấy xuất hiện một số dependency khác cho logging và unit testing (kiểm thử đơn vị), tuy nhiên chúng ta sẽ không sử dụng chúng trong ví dụ này.

File pom.xml cuối cùng của chúng ta trông như dưới đây.

<project xmlns="<https://maven.apache.org/POM/4.0.0>" xmlns:xsi="<https://www.w3.org/2001/XMLSchema-instance>"
	xsi:schemaLocation="<https://maven.apache.org/POM/4.0.0> <https://maven.apache.org/xsd/maven-4.0.0.xsd>">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.springframework.samples</groupId>
	<artifactId>SpringJDBCTransactionManagement</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<properties>

		<!-- Generic properties -->
		<java.version>1.7</java.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

		<!-- Spring -->
		<spring-framework.version>4.0.2.RELEASE</spring-framework.version>

		<!-- Logging -->
		<logback.version>1.0.13</logback.version>
		<slf4j.version>1.7.5</slf4j.version>

		<!-- Test -->
		<junit.version>4.11</junit.version>

	</properties>

	<dependencies>
		<!-- Spring and Transactions -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>

		<!-- Spring JDBC and MySQL Driver -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.0.5</version>
		</dependency>

		<!-- Logging with SLF4J & LogBack -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${slf4j.version}</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>${logback.version}</version>
			<scope>runtime</scope>
		</dependency>

		<!-- Test Artifacts -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${spring-framework.version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>${junit.version}</version>
			<scope>test</scope>
		</dependency>

	</dependencies>
</project>

Nhớ cập nhật Spring lên phiên bản mới nhất cũng như bảo đảm rằng driver cho MySQL database tương thích với phiên bản MySQL bạn đã cài đặt.

Các class Model

Ta sẽ tạo hai Java Bean là CustomerAddress, tương ứng với hai bảng trong database.

package com.journaldev.spring.jdbc.model;

public class Address {

	private int id;
	private String address;
	private String country;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	public String getCountry() {
		return country;
	}
	public void setCountry(String country) {
		this.country = country;
	}
	
}
package com.journaldev.spring.jdbc.model;

public class Customer {

	private int id;
	private String name;
	private Address address;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Address getAddress() {
		return address;
	}
	public void setAddress(Address address) {
		this.address = address;
	}
	
}

Chú ý rằng bean Customer chứa Address như một biến. Khi triển khai DAO cho Customer, ta sẽ xử lý dữ liệu cho cả hai bảng customeraddress, đồng thời thực thi hai câu lệnh INSERT riêng biệt. Đây chính là lý do tại sao ta cần đến transaction management để tránh sự không nhất quán của dữ liệu.

Triển khai DAO

Hãy cùng triển khai DAO cho bean Customer. Để cho đơn giản, ta sẽ chỉ tạo một phương thức duy nhất để chèn bản ghi vào cả hai bảng customeraddress.

package com.journaldev.spring.jdbc.dao;

import com.journaldev.spring.jdbc.model.Customer;

public interface CustomerDAO {

	public void create(Customer customer);
}
package com.journaldev.spring.jdbc.dao;

import javax.sql.DataSource;

import org.springframework.jdbc.core.JdbcTemplate;

import com.journaldev.spring.jdbc.model.Customer;

public class CustomerDAOImpl implements CustomerDAO {

	private DataSource dataSource;

	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	@Override
	public void create(Customer customer) {
		String queryCustomer = "insert into Customer (id, name) values (?,?)";
		String queryAddress = "insert into Address (id, address,country) values (?,?,?)";

		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

		jdbcTemplate.update(queryCustomer, new Object[] { customer.getId(),
				customer.getName() });
		System.out.println("Inserted into Customer Table Successfully");
		jdbcTemplate.update(queryAddress, new Object[] { customer.getId(),
				customer.getAddress().getAddress(),
				customer.getAddress().getCountry() });
		System.out.println("Inserted into Address Table Successfully");
	}

}

Chú ý rằng triển khai CustomerDAO không tự xử lý việc quản lý transaction. Bằng cách này, ta đã phân tách được trách nhiệm cho code bởi vì đôi khi ta nhận được các triển khai DAO từ bên thứ ba và không có quyền kiểm soát các class này.

Service

Tiếp theo, hãy tạo một Customer Service sử dụng triển khai CustomerDAO và áp dụng transaction management khi chèn bản ghi vào cả hai bảng customeraddress trong một phương thức duy nhất.

package com.journaldev.spring.jdbc.service;

import com.journaldev.spring.jdbc.model.Customer;

public interface CustomerManager {

	public void createCustomer(Customer cust);
}
package com.journaldev.spring.jdbc.service;

import org.springframework.transaction.annotation.Transactional;

import com.journaldev.spring.jdbc.dao.CustomerDAO;
import com.journaldev.spring.jdbc.model.Customer;

public class CustomerManagerImpl implements CustomerManager {

	private CustomerDAO customerDAO;

	public void setCustomerDAO(CustomerDAO customerDAO) {
		this.customerDAO = customerDAO;
	}

	@Override
	@Transactional
	public void createCustomer(Customer cust) {
		customerDAO.create(cust);
	}

}

Nếu để ý, bạn sẽ thấy triển khai CustomerManager chỉ đơn thuần sử dụng CustomerDAO để tạo customer. Nhưng nó cung cấp transaction management kiểu declarative bằng cách thêm annotation @Transactional vào phương thức createCustomer(). Đó là tất cả những gì chúng ta cần làm trong code để sử dụng trình quản lý transaction trong Spring.

Annotation @Transactional có thể được áp dụng cho cả phương thức lẫn toàn bộ class. Nếu bạn muốn tất cả các phương thức đều có tính năng quản lý transaction, bạn nên đặt annotation này ở cấp class.

Phần duy nhất còn lại phải làm là cấu hình và liên kết các Spring bean để ví dụ của chúng ta có thể hoạt động.

Cấu hình bean

Tạo một file cấu hình Spring Bean với tên spring.xml. Chúng ta sẽ sử dụng file này trong chương trình test để liên kết các Spring bean và thực thi chương trình JDBC nhằm kiểm tra tính năng quản lý transaction.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="<https://www.springframework.org/schema/beans>"
	xmlns:xsi="<https://www.w3.org/2001/XMLSchema-instance>" xmlns:context="<https://www.springframework.org/schema/context>"
	xmlns:tx="<https://www.springframework.org/schema/tx>"
	xsi:schemaLocation="<https://www.springframework.org/schema/beans> <https://www.springframework.org/schema/beans/spring-beans.xsd>
		<https://www.springframework.org/schema/context> <https://www.springframework.org/schema/context/spring-context-4.0.xsd>
		<https://www.springframework.org/schema/tx> <https://www.springframework.org/schema/tx/spring-tx-4.0.xsd>">

	<!-- 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>
	
	<!-- MySQL DB DataSource -->
	<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/TestDB" />
		<property name="username" value="pankaj" />
		<property name="password" value="pankaj123" />
	</bean>

	<bean id="customerDAO" class="com.journaldev.spring.jdbc.dao.CustomerDAOImpl">
		<property name="dataSource" ref="dataSource"></property>
	</bean>

	<bean id="customerManager" class="com.journaldev.spring.jdbc.service.CustomerManagerImpl">
		<property name="customerDAO" ref="customerDAO"></property>
	</bean>

</beans>

Các điểm quan trọng cần lưu ý trong file cấu hình Spring bean bao gồm:

  • Phần tử tx:annotation-driven được dùng để báo cho Spring context biết rằng chúng ta đang sử dụng cấu hình quản lý transaction dựa trên annotation. Thuộc tính transaction-manager được dùng để cung cấp tên của bean quản lý transaction. Giá trị mặc định của transaction-managertransactionManager, nhưng ở đây ta vẫn khai báo tường minh ra để tránh nhầm lẫn. Thuộc tính proxy-target-class được dùng để yêu cầu Spring context sử dụng proxy (lớp ủy quyền) dựa trên class. Nếu không có thuộc tính này, bạn sẽ gặp lỗi runtime với thông báo như sau:
Exception in thread “main”
org.springframework.beans.factory.BeanNotOfRequiredTypeException:
Bean named ‘customerManager’ must be of type [com.journaldev.spring.jdbc.service.CustomerManagerImpl], but was
actually of type
[com.sun.proxy.$Proxy6]
  • Vì chúng ta đang sử dụng JDBC, ta sẽ tạo một bean transactionManager thuộc kiểu org.springframework.jdbc.datasource.DataSourceTransactionManager. Điều này rất quan trọng và chúng ta nên sử dụng class triển khai transaction manager phù hợp với transaction API đang dùng.
  • Bean *dataSource* được dùng để tạo đối tượng DataSource. Ta cần cung cấp các thuộc tính cấu hình database như driverClassName, url, username và password. Hãy thay đổi các giá trị này cho phù hợp với cài đặt của bạn.
  • Chúng ta đang inject (truyền vào) dataSource vào bean customerDAO. Tương tự, ta inject bean customerDAO vào định nghĩa của bean customerManager.

Khi đã thiết lập xong, hãy tạo một class test đơn giản để kiểm tra việc triển khai quản lý transaction của chúng ta.

package com.journaldev.spring.jdbc.main;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.journaldev.spring.jdbc.model.Address;
import com.journaldev.spring.jdbc.model.Customer;
import com.journaldev.spring.jdbc.service.CustomerManager;
import com.journaldev.spring.jdbc.service.CustomerManagerImpl;

public class TransactionManagerMain {

	public static void main(String[] args) {
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
				"spring.xml");

		CustomerManager customerManager = ctx.getBean("customerManager",
				CustomerManagerImpl.class);

		Customer cust = createDummyCustomer();
		customerManager.createCustomer(cust);

		ctx.close();
	}

	private static Customer createDummyCustomer() {
		Customer customer = new Customer();
		customer.setId(2);
		customer.setName("Pankaj");
		Address address = new Address();
		address.setId(2);
		address.setCountry("India");
		// setting value more than 20 chars, so that SQLException occurs
		address.setAddress("Albany Dr, San Jose, CA 95129");
		customer.setAddress(address);
		return customer;
	}

}

Chú ý rằng ta đang cố tình đặt giá trị cho cột address quá dài để chương trình gây ra lỗi (exception) khi chèn dữ liệu vào bảng Address. Bây giờ, khi chạy chương trình test, ta sẽ nhận được output như sau.

Mar 29, 2014 7:59:32 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3fa99295: startup date [Sat Mar 29 19:59:32 PDT 2014]; root of context hierarchy
Mar 29, 2014 7:59:32 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring.xml]
Mar 29, 2014 7:59:32 PM org.springframework.jdbc.datasource.DriverManagerDataSource setDriverClassName
INFO: Loaded JDBC driver: com.mysql.jdbc.Driver
Inserted into Customer Table Successfully
Mar 29, 2014 7:59:32 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [org/springframework/jdbc/support/sql-error-codes.xml]
Mar 29, 2014 7:59:32 PM org.springframework.jdbc.support.SQLErrorCodesFactory <init>
INFO: SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase]
Exception in thread "main" org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [insert into Address (id, address,country) values (?,?,?)]; Data truncation: Data too long for column 'address' at row 1; nested exception is com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data too long for column 'address' at row 1
	at org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:100)
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:73)
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
	at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:658)
	at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:907)
	at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:968)
	at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:978)
	at com.journaldev.spring.jdbc.dao.CustomerDAOImpl.create(CustomerDAOImpl.java:27)
	at com.journaldev.spring.jdbc.service.CustomerManagerImpl.createCustomer(CustomerManagerImpl.java:19)
	at com.journaldev.spring.jdbc.service.CustomerManagerImpl$$FastClassBySpringCGLIB$$84f71441.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:711)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644)
	at com.journaldev.spring.jdbc.service.CustomerManagerImpl$$EnhancerBySpringCGLIB$$891ec7ac.createCustomer(<generated>)
	at com.journaldev.spring.jdbc.main.TransactionManagerMain.main(TransactionManagerMain.java:20)
Caused by: com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data too long for column 'address' at row 1
	at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2939)
	at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1623)
	at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1715)
	at com.mysql.jdbc.Connection.execSQL(Connection.java:3249)
	at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1268)
	at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1541)
	at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1455)
	at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1440)
	at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:914)
	at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:907)
	at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:642)
	... 16 more

Chú ý rằng thông báo log cho biết dữ liệu đã được chèn thành công vào bảng customer, nhưng lỗi từ driver MySQL cũng nói rõ rằng giá trị cho cột address quá dài. Bây giờ, nếu kiểm tra bảng Customer, bạn sẽ không tìm thấy bất kỳ bản ghi nào ở đó. Điều này có nghĩa là transaction đã được rollback hoàn toàn.Nếu bạn đang tự hỏi cơ chế quản lý transaction hoạt động như thế nào, hãy xem kỹ các dòng log và để ý đến các class AOP và Proxy được tạo bởi Spring. Framework này ang sử dụng Around advice (mã can thiệp trước vào sau code) để tạo một lớp proxy cho CustomerManagerImpl và chỉ commit transaction khi phương thức thực thi thành công. Nếu có bất kỳ lỗi nào xảy ra, nó sẽ rollback toàn bộ transaction.

Tổng kết

Bạn có thể tải code của dự án quản lý JDBC transaction mẫu ở trên từ liên kết này và thử nghiệm với nó thêm để tìm hiểu sâu hơn. Nếu có nhu cầu tìm hiểu thêm về mô hình lập trình hướng khía cạnh (AOP), hãy tìm đọc thêm bài hướng dẫn của chúng tôi về Spring AOP để biết cách giúp tách biệt các chức năng, nâng cao hiệu quả quản lý transaction một cách tự động và linh hoạt hơn.

0 Bình luận

Đăng nhập để thảo luận

Chuyên mục Hướng dẫn

Tổng hợp các bài viết hướng dẫn, nghiên cứu và phân tích chi tiết về kỹ thuật, các xu hướng công nghệ mới nhất dành cho lập trình viên.

Đăng ký nhận bản tin của chúng tôi

Hãy trở thành người nhận được các nội dung hữu ích của CyStack sớm nhất

Xem chính sách của chúng tôi Chính sách bảo mật.

Đăng ký nhận Newsletter

Nhận các nội dung hữu ích mới nhất