一、事务的基本概念
1、什么是事务
事务是一个由一组操作组成的逻辑单元,这组操作要么都成功执行,要么都回滚,保证数据的一致性和完整性。
2、事务的基本特性
原子性(Atomicity):事务作为一个整体操作,要么全部成功执行,要么全部回滚,不存在部分执行的情况。
一致性(Consistency):事务执行前和执行后都必须保持数据的一致性。即使在事务执行过程中出现异常,也会使数据保持一致。
隔离性(Isolation):隔离性指的是多个并发的事务之间要互相隔离,每个事务的执行都是独立的,并且不受其他事务的干扰。
持久性(Durability):持久性指的是事务一旦执行成功,对数据的修改就会被持久化到数据库中,并且对后续的数据访问是可见的。
3、事务的隔离级别
读未提交(Read Uncommitted):最低级别的隔离级别,一个事务可以看到其他事务尚未提交的修改。
读已提交(Read Committed):一个事务只能看到其他事务已经提交的修改,保证不会读到脏数据,但可能会出现不可重复读。
可重复读(Repeatable Read):一个事务开始后,能够多次读取同一个数据,并且每次读到的值都一样。保证了在事务运行期间,其他事务对于该数据的修改不可见,但仍然可能出现幻读问题(即一个事务多次查询同一范围的数据,结果却不一致)。
串行化(Serializable):最高级别的隔离级别,事务之间完全隔离,可以避免脏读、不可重复读、幻读的问题。但是串行化并发性较低,会影响系统的并发性能。
4、三大读问题
脏读(Dirty Read)是指一个事务读取了另一个事务尚未提交的数据。例如,事务A修改了某个数据,但还未提交,然后事务B读取了这个未提交的数据。如果事务A最终回滚了,则事务B读取到的数据实际上是无效的。脏读可能会导致不一致的数据状态。
重复读(Non-Repeatable Read)是指一个事务在多次读取同一数据时,得到了不同的结果。例如,事务A读取了某个数据,然后事务B修改了这个数据并提交,事务A再次读取同一数据,得到了不同的结果。重复读可能会导致事务之间的数据不一致。
幻读(Phantom Read)是指一个事务在多次查询同一范围的数据时,得到了不同的结果。例如,事务A按某个条件范围查询了一组数据,然后事务B新增了符合该条件的数据并提交,事务A再次按同一条件查询,发现结果集比之前查询时多了一条或多条数据。幻读问题主要涉及到范围查询中新增或删除操作引起的不一致。
二、基于注解的声名式事务
1、 @Transactional代码实现
package com.toonyoo.spring.transaction.service;
import com.toonyoo.spring.transaction.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
@Transactional
@Override
public void buyBookById(Integer bookId, Integer userId) {
// 根据图书id查询图书价格
BigDecimal bookPrice = bookDao.getBookPrice(bookId);
// 图书库存-1
bookDao.updateBookStock(bookId);
// 发生异常 测试事务是否回滚
int i = 1 / 0;
// 用户余额 - 图书价格
bookDao.updateUserBalance(userId, bookPrice);
}
}
2、 @Transactional 事务属性
- 传播行为
- 隔离级别
- 只读
- 异常类型回滚
3、事务的传播行为
事务的传播行为(Propagation)是指在多个事务方法相互调用的情况下,事务是如何进行传播和合并的。在Spring中,事务的传播行为由@Transactional注解的propagation属性决定。
事务的传播行为包括以下几种类型:
- REQUIRED(默认):如果当前存在事务,则加入该事务中执行。如果当前没有事务,则创建一个新的事务。
- SUPPORTS:如果当前存在事务,则加入该事务中执行。如果当前没有事务,则以非事务方式执行。
- NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,则挂起该事务。
- REQUIRES_NEW:创建一个新的事务,并且如果当前存在事务,则将其挂起。
- MANDATORY:如果当前存在事务,则加入该事务中执行。如果当前没有事务,则抛出异常。
- NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
- NESTED:如果当前存在事务,则在嵌套事务中执行。如果当前没有事务,则创建一个新的事务。
这些传播行为可以根据实际情况来选择。在方法之间的调用过程中,不同传播行为的组合可以产生不同的事务行为。
例如,假设有一个方法A,该方法有一个REQUIRED传播行为的事务注解。在方法A中调用了另一个方法B,两个方法都有事务注解。根据REQUIRED传播行为的定义,方法B将加入到方法A的事务中执行,即它们共享同一个事务。如果方法B执行过程中发生异常,则整个事务将回滚;如果方法B执行正常结束,则整个事务将提交。