Spring-事务
1.事务基本概念
1.1. 什么是事务:
数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。
1.2. 事务特性ACID:
- 原子性(Atomicity):事务作为一个整体操作,要么全部执行成功,要么全部失败回滚,不允许部分执行成功部分失败。
- 一致性(Consistency):事务执行前后,数据库从一个一致状态转换为另一个一致状态。事务执行过程中,数据库的数据保持一致性约束。
- 隔离性(Isolation):事务的执行应该与其他事务隔离开来,每个事务在执行过程中看到的数据应该是一致的。事务隔离级别包括读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)和串行化(Serializable)。
- 持久性(Durability):一旦事务提交,对数据库的修改应该是永久性的,即使发生系统故障也不会丢失。
2. Spring事务管理概述
Spring框架通过对AOP(面向切面编程)的支持来实现事务管理。在Spring中,事务管理是通过在方法执行前后应用拦截器(即事务通知)来实现的。Spring事务管理是Spring框架提供的一种机制,用于管理应用程序中的事务操作。它提供了一种简单而强大的方式来处理数据库操作的事务性需求,并确保数据的一致性和完整性。
2.1. Spring事务的原理可以概括为以下几个关键点:
-
事务管理器(TransactionManager):事务管理器负责管理事务的生命周期,包括事务的开始、提交和回滚操作。Spring支持多种事务管理器,如JDBC事务管理器、Hibernate事务管理器、JTA事务管理器等。
-
事务切入点(TransactionInterceptor):事务切入点定义了哪些方法需要应用事务。通过配置切入点,可以将事务逻辑织入到目标方法中。
-
事务通知(TransactionAdvice):事务通知是指在方法执行前后,执行相关的事务操作。事务通知包括事务的开始、提交和回滚等操作。
-
事务属性(TransactionAttribute):事务属性定义了事务的行为,包括事务的隔离级别、传播行为、只读属性、超时设置等。通过事务属性,可以灵活地配置事务的特性。
-
事务管理器和事务通知的绑定:Spring框架将事务管理器和事务通知进行绑定,通过AOP代理技术,在方法执行前后应用事务通知,并委托给事务管理器来管理事务的生命周期。
2.2. 使用Spring事务管理的好处包括:
- 简化事务管理:通过声明式事务管理,可以将事务管理的逻辑从业务代码中分离出来,使业务代码更加清晰和简洁。
- 提供灵活性和可扩展性:通过配置不同的事务属性,可以灵活地控制事务的行为,满足不同场景下的需求。
- 支持多种数据访问技术:Spring事务管理可以与各种数据访问技术(如JDBC、Hibernate、JPA等)集成使用,提供统一的事务管理机制。
总的来说,Spring事务管理是一个强大而灵活的机制,能够帮助开发者实现可靠的事务处理,保证数据的一致性和完整性,并提供了简化和统一的事务管理方式。
2.3. Spring事务管理支持以下几种事务管理方式:
-
编程式事务管理:通过编写代码显式地控制事务的开始、提交或回滚。这种方式需要手动管理事务的边界,可以实现对事务的细粒度控制。
-
声明式事务管理:通过配置元数据(例如XML配置文件或注解)来定义事务的属性,Spring框架会根据这些配置自动管理事务的边界。这种方式更加方便和简洁,减少了代码的侵入性。
3. 编程式事务管理
一般不用,主要使用声明事务管理.
如果是分布式系统,使用分布式事务管理。
编程式事务管理是一种使用编程代码显式控制事务边界的方式。在Spring中,可以使用编程式事务管理来管理事务的开始、提交和回滚操作,以及设置事务的属性。
下面是使用编程式事务管理的基本步骤:
- 获取事务管理器(TransactionManager):首先,需要获取一个事务管理器的实例,它负责管理事务的开始、提交和回滚操作。可以通过Spring的依赖注入机制获取事务管理器的实例。
- 创建事务定义(TransactionDefinition):接下来,需要创建一个事务定义对象,用于定义事务的属性,包括事务的隔离级别、传播行为、超时时间等。可以通过编程方式创建一个事务定义对象。
- 开启事务:在需要开启事务的方法或代码块之前,调用事务管理器的
beginTransaction()
方法来开启事务。这将创建一个新的事务,并将其关联到当前的线程。 - 执行业务逻辑:在事务范围内,执行需要进行事务管理的业务逻辑代码。
- 判断事务结果并提交或回滚:根据业务逻辑的执行结果,决定是提交事务还是回滚事务。可以使用事务管理器的
commit()
方法提交事务,或者使用rollback()
方法回滚事务。
下面是一个使用编程式事务管理的示例:
@Autowired
private PlatformTransactionManager transactionManager;
public void performTransaction() {
// 获取事务定义
DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
transactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
// 开启事务
TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
try {
// 执行业务逻辑
// ...
// 判断业务逻辑结果并提交事务
if (businessLogicSuccessful) {
transactionManager.commit(transactionStatus);
} else {
transactionManager.rollback(transactionStatus);
}
} catch (Exception e) {
// 异常处理并回滚事务
transactionManager.rollback(transactionStatus);
throw e;
}
}
编程式事务管理的优点是可以实现对事务的细粒度控制,可以根据需要在代码的任意位置开启、提交或回滚事务。然而,它的缺点是代码的侵入性较高,需要在每个需要事务管理的方法中手动管理事务的边界。因此,对于大型应用或复杂的事务逻辑,声明式事务管理更为常用和方便。
4. 声明式事务管理
声明式事务管理是一种通过配置的方式来管理事务的边界,而不需要在业务代码中显式地编写事务管理的代码。在Spring框架中,可以使用声明式事务管理来实现事务的开始、提交和回滚操作,以及设置事务的属性。
下面是使用声明式事务管理的基本步骤:
- 配置事务管理器(TransactionManager):首先,需要配置一个事务管理器的实例,它负责管理事务的开始、提交和回滚操作。可以通过Spring的配置文件或注解方式进行配置。
- 配置事务通知(Transaction Advice):接下来,需要配置一个事务通知,用于在方法执行前后进行事务的开启、提交和回滚操作。可以使用Spring的事务注解(如@Transactional)或基于XML的配置方式来配置事务通知。
- 配置事务切入点(Transaction Pointcut):事务切入点用于指定哪些方法需要应用事务管理。可以使用Spring的切入点表达式(如@Pointcut注解或XML配置)来定义事务切入点。
- 执行业务逻辑:在需要进行事务管理的方法中,执行业务逻辑代码。当方法被调用时,声明式事务管理会根据配置的事务通知和切入点进行事务的管理。
下面是一个使用声明式事务管理的示例:
@Transactional
public void performTransaction() {
// 执行业务逻辑
// ...
}
在上面的示例中,使用了@Transactional
注解来标记方法,表示该方法需要进行事务管理。当方法被调用时,声明式事务管理会根据配置的事务通知和切入点,在方法执行前开启事务,在方法执行后根据方法的返回值决定是提交事务还是回滚事务。
声明式事务管理的优点是可以将事务管理与业务逻辑解耦,简化了代码的编写,提高了代码的可维护性和可读性。同时,它还提供了灵活的事务配置方式,可以通过注解或XML进行配置。然而,声明式事务管理的缺点是对于一些复杂的事务场景可能不够灵活,无法实现细粒度的事务控制。在这种情况下,可以结合使用编程式事务管理来满足特定需求。
总结来说,声明式事务管理适用于大多数简单的事务场景,可以通过简单的配置实现事务管理,提高开发效率和代码的可读性。对于复杂的事务场景,可以使用编程式事务管理或结合两种方式来满足需求。
4.1. 模拟场景:
用户购买图书,先查询图书的价格,再更新图书的库存和用户的余额
假设用户id为1的用户,购买id为1的图书
用户余额为50,而图书价格为80
购买图书之后,用户的余额为-30,数据库中余额字段设置了无符号,因此无法将-30插入到余额字段
此时执行sql语句会抛出SQLException
4.2. 准备工作:
1)涉及环境:
JDK17
Spring6
MySQL8
2)涉及pom:
<dependencies>
<!--spring context依赖-->
<!--当你引入Spring Context依赖之后,表示将Spring的基础依赖引入了-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.2</version>
</dependency>
<!--spring对junit的支持相关依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>6.0.2</version>
</dependency>
<!--junit5测试-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.3.1</version>
</dependency>
<!--spring jdbc Spring 持久化层支持jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>6.0.2</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<!-- druid数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.15</version>
</dependency>
<!--log4j2的依赖-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.19.0</version>
</dependency>
</dependencies>
3)数据库表:
CREATE DATABASE `spring6`;
use `spring6`;
CREATE TABLE `t_book` (
`book_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`book_name` varchar(20) DEFAULT NULL COMMENT '图书名称',
`price` int(11) DEFAULT NULL COMMENT '价格',
`stock` int(10) unsigned DEFAULT NULL COMMENT '库存(无符号)',
PRIMARY KEY (`book_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
insert into `t_book`(`book_id`,`book_name`,`price`,`stock`) values (1,'斗破苍穹',80,100),(2,'斗罗大陆',50,100);
CREATE TABLE `t_user` (
`user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`username` varchar(20) DEFAULT NULL COMMENT '用户名',
`balance` int(10) unsigned DEFAULT NULL COMMENT '余额(无符号)',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
insert into `t_user`(`user_id`,`username`,`balance`) values (1,'admin',50);
4.3. 配置类:
扫描组件:包路径换成自己项目
MySQL:换成自己数据库配置
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration
@ComponentScan("com.xxx")
@EnableTransactionManagement
public class SpringConfig {
@Bean
public DataSource getDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/spring?characterEncoding=utf8&useSSL=false");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
@Bean(name = "jdbcTemplate")
public JdbcTemplate getJdbcTemplate(DataSource dataSource){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}
4.4. 事务使用:
BookController
@Controller
public class BookController {
@Autowired
private BookService bookService;
public void buyBook(Integer bookId, Integer userId){
bookService.buyBook(bookId, userId);
}
}
BookService
public interface BookService {
void buyBook(Integer bookId, Integer userId);
}
BookServiceImpl
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
@Override
public void buyBook(Integer bookId, Integer userId) {
//查询图书的价格
Integer price = bookDao.getPriceByBookId(bookId);
//更新图书的库存
bookDao.updateStock(bookId);
//更新用户的余额
bookDao.updateBalance(userId, price);
}
}
BookDao
public interface BookDao {
Integer getPriceByBookId(Integer bookId);
void updateStock(Integer bookId);
void updateBalance(Integer userId, Integer price);
}
BookDaoImpl
@Repository
public class BookDaoImpl implements BookDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public Integer getPriceByBookId(Integer bookId) {
String sql = "select price from t_book where book_id = ?";
return jdbcTemplate.queryForObject(sql, Integer.class, bookId);
}
@Override
public void updateStock(Integer bookId) {
String sql = "update t_book set stock = stock - 1 where book_id = ?";
jdbcTemplate.update(sql, bookId);
}
@Override
public void updateBalance(Integer userId, Integer price) {
String sql = "update t_user set balance = balance - ? where user_id = ?";
jdbcTemplate.update(sql, price, userId);
}
}
4.5. 测试:
public class TxByAllAnnotationTest {
@Test
public void testTxAllAnnotation(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
BookController accountService = applicationContext.getBean("bookController", BookController.class);
accountService.buyBook(1, 1);
}
}
4.6. 结果:
1)操作前数据库表数据:
用户admin余额50、斗破库存100、斗罗库存100。
2)没有加@Transactional
注解:
用户admin,买斗破苍穹和斗罗时,由于在买斗罗大陆时由于数据库字符约束unsigned
限制,导致数据异常。用户余额还是50,斗破苍穹库存为99,斗罗大陆库存100,凭空少了一本斗破。
3)加了@Transactional
注解:
在buyBook方法上加了@Transactional
注解
@Transactional
@Override
public void buyBook(Integer bookId, Integer userId) {
//查询图书的价格
Integer price = bookDao.getPriceByBookId(bookId);
//更新图书的库存
bookDao.updateStock(bookId);
//更新用户的余额
bookDao.updateBalance(userId, price);
}
用户admin,买斗破苍穹和斗罗时,由于在买斗罗大陆时由于数据库字符约束unsigned
限制,导致数据异常。事务回滚,用户余额还是50,斗破苍穹库存为100,斗罗大陆库存100。
5. Spring事务管理的属性配置
5.1. 只读
指定事务是否为只读事务。如果只读事务中没有写操作,可以提高并发性能。
@Transactional(readOnly = true)
对增删改操作设置只读会抛出下面异常:
Caused by: java.sql.SQLException: Connection is read-only.
Queries leading to data modification are not allowed
5.2. 超时
指定事务的最大执行时间,超过该时间将自动回滚事务。
//超时时间单位秒
@Transactional(timeout = 3)
执行过程中抛出异常:
org.springframework.transaction.TransactionTimedOutException:
Transaction timed out: deadline was Fri Jun 04 16:25:39 CST 2022
5.3. 回滚策略
用于定义事务回滚的规则。可以指定特定的异常类型,当出现该异常时进行事务回滚。
可以通过@Transactional中相关属性设置回滚策略
- rollbackFor属性:需要设置一个Class类型的对象
- rollbackForClassName属性:需要设置一个字符串类型的全类名
- noRollbackFor属性:需要设置一个Class类型的对象
- rollbackFor属性:需要设置一个字符串类型的全类名
@Transactional(noRollbackFor = ArithmeticException.class)
5.4. 隔离级别
指定事务的隔离级别,用于控制并发事务之间的互相影响。
隔离级别一共有四种:
@Transactional(isolation = Isolation.DEFAULT)//使用数据库默认的隔离级别
@Transactional(isolation = Isolation.READ_UNCOMMITTED)//读未提交
@Transactional(isolation = Isolation.READ_COMMITTED)//读已提交
@Transactional(isolation = Isolation.REPEATABLE_READ)//可重复读
@Transactional(isolation = Isolation.SERIALIZABLE)//串行化
各个隔离级别解决并发问题的能力:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ UNCOMMITTED | 有 | 有 | 有 |
READ COMMITTED | 无 | 有 | 有 |
REPEATABLE READ | 无 | 无 | 有 |
SERIALIZABLE | 无 | 无 | 无 |
5.5. 传播行为
定义事务在方法调用链中的传播方式。常用的传播行为有REQUIRED、REQUIRES_NEW等。
例如,当一个方法调用另一个方法时,如何处理事务边界。在service类中有a()方法和b()方法,a()方法上有事务,b()方法上也有事务,当a()方法执行过程中调用了b()方法,事务是如何传递的?合并到一个事务里?还是开启一个新的事务?这就是事务传播行为。
一共有七种传播行为:
- REQUIRED:支持当前事务,如果不存在就新建一个(默认)【没有就新建,有就加入】
- SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行**【有就加入,没有就不管了】**
- MANDATORY:必须运行在一个事务中,如果当前没有事务正在发生,将抛出一个异常**【有就加入,没有就抛异常】**
- REQUIRES_NEW:开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起**【不管有没有,直接开启一个新事务,开启的新事务和之前的事务不存在嵌套关系,之前事务被挂起】**
- NOT_SUPPORTED:以非事务方式运行,如果有事务存在,挂起当前事务**【不支持事务,存在就挂起】**
- NEVER:以非事务方式运行,如果有事务存在,抛出异常**【不支持事务,存在就抛异常】**
- NESTED:如果当前正有一个事务在进行中,则该方法应当运行在一个嵌套式事务中。被嵌套的事务可以独立于外层事务进行提交或回滚。如果外层事务不存在,行为就像REQUIRED一样。【有事务的话,就在这个事务里再嵌套一个完全独立的事务,嵌套的事务可以独立的提交和回滚。没有事务就和REQUIRED一样。】
// 默认情况,表示如果当前线程上有已经开启的事务可用,那么就在这个事务中运行。
@Transactional(propagation = Propagation.REQUIRED)
// 表示不管当前线程上是否有已经开启的事务,都要开启新事务
@Transactional(propagation = Propagation.REQUIRES_NEW)
6. Spring事务管理的最佳实践
在使用Spring进行事务管理时,以下是一些最佳实践:
- 尽量将事务控制范围缩小:只在必要的方法或方法调用链上添加事务注解,避免过大的事务范围。这样可以减小锁的粒度,提高并发性能。
- 使用声明式事务管理:优先使用声明式事务管理,通过
@Transactional
注解或XML配置来定义事务属性。这样可以将事务的配置与业务逻辑分离,使代码更加清晰。 - 考虑事务的隔离级别:根据业务需求和性能要求选择合适的事务隔离级别。通常情况下,使用默认的隔离级别(READ_COMMITTED)即可满足大多数需求。如果有特殊需求,可以选择其他级别。
- 设置合理的事务超时时间:根据业务逻辑和性能考虑,设置合理的事务超时时间。如果事务执行时间过长,可以考虑调优或优化相关操作。
- 捕获和处理异常:在事务方法中捕获和处理异常,避免事务回滚后无法正确处理异常情况。可以根据实际业务需求,选择回滚或手动处理异常。
- 选择合适的事务传播行为:根据方法调用链的需求,选择合适的事务传播行为。例如,如果需要在当前事务内部调用另一个方法,可以选择REQUIRED传播行为。
- 使用只读事务:对于只读操作,可以使用只读事务,可以提高并发性能。只读事务不会对数据进行修改,因此可以减少锁竞争。
- 监控和调优:监控事务的性能和运行情况,根据需要进行调优。可以使用Spring提供的事务管理相关的统计信息和监控工具。
- 使用合适的事务管理器:根据具体的数据访问技术和需求,选择合适的事务管理器。例如,对于JDBC事务管理,可以使用
DataSourceTransactionManager
。 - 进行单元测试:针对事务的业务逻辑编写单元测试,确保事务的正确性和稳定性。
综上所述,通过遵循这些最佳实践,可以更好地使用Spring进行事务管理,确保事务的一致性、隔离性和可靠性。同时,也可以根据具体业务需求进行适当的调整和优化。
7. 事务失效场景
事务失效是指在使用事务管理时,事务的一些特性无法正常工作,导致事务不起作用或无法达到预期的结果。以下是一些常见的事务失效场景:
- 方法没有被正确标注为事务:在使用声明式事务管理时,可能会忘记在方法上添加
@Transactional
注解或XML配置,导致事务管理器无法识别和管理该方法的事务。 - 异常未被捕获或处理:当事务方法中的异常未被捕获或处理时,事务可能会被回滚,但异常信息没有被正确处理,导致事务失效。
- 外部方法调用内部事务方法:当一个事务方法内部调用另一个事务方法时,默认情况下,内部方法将在同一个事务中执行。但如果外部方法没有被标记为事务,或者内部方法的传播行为设置不正确,可能导致内部方法的事务失效。
- 不支持的数据库操作:某些数据库操作不支持事务,例如DDL语句(如创建表、修改表结构等)。在执行这些操作时,事务管理器无法控制事务的提交和回滚,导致事务失效。
- 事务超时:如果事务执行时间过长,超过了事务管理器设置的超时时间,事务可能会被自动回滚,导致事务失效。
- 多线程环境下的事务:在多线程环境下,如果事务方法中使用了共享变量或资源,并且没有正确处理线程安全性,可能导致事务失效或数据不一致。
- 多个数据源的事务:在使用多个数据源时,如果事务管理器没有正确配置或选择合适的事务管理策略,可能导致事务失效或数据不一致。
为了避免事务失效,应该仔细理解事务的特性和工作原理,并根据具体场景和需求正确配置和使用事务管理器。同时,编写单元测试和进行全面的测试,以确保事务在各种情况下都能正常工作。
8. 总结
事务是数据库操作中的重要概念,它可以确保一组数据库操作要么全部成功提交,要么全部回滚到初始状态。事务的目的是保证数据的一致性、完整性和可靠性。
以下是事务的一些关键点和总结:
- 事务的特性(ACID):事务具有原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)这四个特性。原子性表示事务要么全部成功,要么全部回滚;一致性表示事务执行前后数据库的状态保持一致;隔离性表示事务之间相互隔离,互不干扰;持久性表示一旦事务提交,对数据库的修改将永久保存。
- 事务的隔离级别:数据库提供了多个隔离级别,包括读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)和串行化(Serializable)。不同的隔离级别提供了不同的事务隔离程度,可以根据具体需求选择合适的隔离级别。
- 事务管理的方式:事务可以通过编程式事务管理和声明式事务管理两种方式来管理。编程式事务管理是在代码中显式地开启、提交和回滚事务;声明式事务管理是通过配置或注解来定义事务的属性和行为,由事务管理器自动管理事务。
- 事务的传播行为:在使用声明式事务管理时,可以设置事务的传播行为来控制事务的边界和范围。常见的传播行为包括REQUIRED、REQUIRES_NEW、NESTED等,每种传播行为都有不同的事务处理方式。
- 事务的失效和注意事项:事务可能会因为一些原因失效,导致事务不起作用或产生意外的结果。在使用事务时,需要注意异常处理、事务边界的设置、多线程环境下的事务管理等方面,以避免事务失效。
- 分布式事务:在分布式系统中,涉及多个数据库或服务的事务称为分布式事务。分布式事务的管理比较复杂,常见的解决方案包括两阶段提交(2PC)、补偿事务(TCC)和消息队列等。
总而言之,事务是保证数据库操作的一致性和可靠性的重要机制。正确理解和使用事务,结合合适的事务管理方式和隔离级别,可以提供可靠的数据操作和高效的应用程序。
9. 参考
- 尚硅谷-Spring6