事务Transaction
事务(Transaction)是数据库管理系统中的一个重要概念,它是一组操作的集合,这些操作要么全部成功,要么全部失败,以确保数据的完整性和一致性。
一、事务的特性(ACID)
-
原子性(Atomicity)
-
原子性是指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败。例如,在一个银行转账的事务中,从账户A向账户B转账100元,这个事务包括两个操作:从账户A减去100元和向账户B加上100元。如果在执行过程中,账户A已经减去了100元,但由于网络故障等原因,账户B没有加上100元,那么这个事务就会失败,系统会回滚到事务开始之前的状态,账户A的钱不会被扣除。
-
-
一致性(Consistency)
-
一致性是指事务执行前后,数据库的完整性约束没有被破坏。它保证了事务的执行使数据库从一个一致的状态转换到另一个一致的状态。例如,在一个订单管理系统中,订单表和订单明细表之间存在外键约束。如果事务试图插入一个不存在的订单编号到订单明细表中,那么这个事务就会违反一致性约束,事务会失败。
-
-
隔离性(Isolation)
-
隔离性是指多个事务并发执行时,一个事务的执行不应受到其他事务的干扰。数据库系统提供了不同的隔离级别来控制事务之间的隔离程度。常见的隔离级别有:
-
读未提交(Read Uncommitted):最低的隔离级别,一个事务可以读取到其他事务未提交的数据。这可能会导致脏读(读取到其他事务尚未提交的错误数据)。
-
读已提交(Read Committed):一个事务只能读取到其他事务已经提交的数据,避免了脏读,但可能会出现不可重复读(在同一个事务中,多次读取同一数据得到的结果不同)。
-
可重复读(Repeatable Read):保证在同一个事务中,多次读取同一数据的结果是一致的,避免了不可重复读,但可能会出现幻读(事务A读取到的某几行数据在事务B的插入操作下,再次读取时出现了新的数据行)。
-
可串行化(Serializable):最高的隔离级别,事务串行执行,避免了脏读、不可重复读和幻读,但并发性能较差。
-
-
-
持久性(Durability)
-
持久性是指事务一旦提交,对数据库的改变就是永久的,即使系统出现故障也不会丢失。例如,当一个事务提交后,即使数据库服务器突然断电,再次启动后,事务所做的一切改变仍然会被保留。
-
二、事务的控制语句
在关系型数据库(如MySQL、PostgreSQL等)中,事务的控制语句主要包括以下几个:
-
开启事务
-
在MySQL中,可以通过
START TRANSACTION
或BEGIN
语句开启一个事务。
-
-
提交事务
-
当事务中的所有操作都执行成功后,可以通过
COMMIT
语句提交事务,使事务中的所有操作永久生效。
-
-
回滚事务
-
如果事务中的某个操作失败,可以通过
ROLLBACK
语句回滚事务,撤销事务中已经执行的操作,使数据库恢复到事务开始之前的状态。
-
-
设置保存点
-
在事务中,可以使用
SAVEPOINT
语句设置保存点,以便在需要时可以回滚到某个保存点。 -
如果需要回滚到某个保存点,可以使用
ROLLBACK TO
语句。
-
三、事务的应用场景
-
金融领域
-
在银行转账、股票交易等金融业务中,事务的使用至关重要。例如,当用户A向用户B转账时,需要确保从用户A的账户中扣除相应的金额,并且将相同的金额添加到用户B的账户中。如果其中一个操作失败,整个事务就会回滚,避免了资金的错误转移。
-
-
电子商务领域
-
在电商平台的订单处理过程中,事务可以确保订单的创建、库存的更新和支付状态的更新等操作的原子性和一致性。例如,当用户下单时,需要检查库存是否充足,如果库存不足,则整个订单创建事务会失败,避免了超卖的情况。
-
-
医疗信息系统
-
在医疗信息系统的患者病历更新、药品库存管理等操作中,事务可以保证数据的完整性和一致性。例如,当医生为患者更新病历时,需要确保所有的检查结果、诊断信息等都正确更新,如果其中一个操作失败,整个事务会回滚,避免了病历信息的错误。
-
Spring框架@Transaction注释
在Spring框架里,我们通过@Transaction注释,实现事务管理,当然底层实现还是基于数据库事务的。
事务传播行为定义了当一个事务方法被另一个事务方法调用时,事务如何传播。Spring提供了多种传播行为,每种行为都有不同的事务处理方式。
事务隔离级别定义了多个事务并发执行时的隔离程度。
事务回滚规则定义了在哪些异常情况下事务会被回滚。
这种就是最简单的@Transaction注释用法
使用的是默认配置:
- 传播行为:REQUIRED(默认)
- 隔离级别:DEFAULT(使用数据库默认隔离级别)
- 回滚规则:默认只对RuntimeException和Error进行回滚
一、事务传播行为(Propagation Behavior)
Propagation 传播
@Transactional(propagation = Propagation.REQUIRED)
事务传播行为定义了当一个事务方法被另一个事务方法调用时,事务如何传播。Spring提供了多种传播行为,每种行为都有不同的事务处理方式。
1. REQUIRED(默认)
-
描述:如果当前存在事务,则加入该事务;如果当前没有事务,则开启一个新的事务。
-
场景:这是最常用的传播行为。适用于大多数业务场景,确保方法调用时始终在事务上下文中执行。
-
例子
@Transactional(propagation = Propagation.REQUIRED) public void methodA() {// 调用methodBmethodB(); }@Transactional(propagation = Propagation.REQUIRED) public void methodB() {// 业务逻辑 }
-
methodA被调用,然后里面的methodB被调用。这时候A是外部事物,B是内部事务,例子里,A有Transactional有事务,就是B的外部存在事务A,所以B融入到A的事务里。
-
如果A没有事务,B就新建事务
-
2. SUPPORTS
-
描述:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。
-
场景:适用于那些不需要事务,但可以加入现有事务的场景。
-
例子:
@Transactional(propagation = Propagation.REQUIRED) public void methodA() {// 调用methodBmethodB(); }@Transactional(propagation = Propagation.SUPPORTS) public void methodB() {// 业务逻辑 }
-
如果
methodA
中存在事务,则methodB
会加入到这个事务中。 -
如果
methodA
中没有事务,则methodB
会以非事务方式执行。
-
3. MANDATORY
-
描述:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
-
场景:适用于那些必须在事务上下文中执行的场景。
-
例子:
@Transactional(propagation = Propagation.REQUIRED) public void methodA() {// 调用methodBmethodB(); }@Transactional(propagation = Propagation.MANDATORY) public void methodB() {// 业务逻辑 }
-
如果
methodA
中存在事务,则methodB
会加入到这个事务中。 -
如果
methodA
中没有事务,则会抛出TransactionRequiredException
。
-
4. REQUIRES_NEW
-
描述:开启一个新的事务,如果当前存在事务,则暂停当前事务。
-
场景:适用于那些需要独立事务的场景,例如在日志记录或审计操作中。
-
例子
@Transactional(propagation = Propagation.REQUIRED) public void methodA() {// 调用methodBmethodB(); }@Transactional(propagation = Propagation.REQUIRES_NEW) public void methodB() {// 业务逻辑 }
-
如果
methodA
中存在事务,则methodB
会开启一个新的事务,methodA
的事务会被暂停。 -
如果
methodA
中没有事务,则methodB
会开启一个新的事务。
-
5. NOT_SUPPORTED
-
描述:以非事务方式执行,如果当前存在事务,则暂停当前事务。
-
场景:适用于那些不需要事务的场景,例如读取配置文件等。
-
例子
@Transactional(propagation = Propagation.REQUIRED) public void methodA() {// 调用methodBmethodB(); }@Transactional(propagation = Propagation.NOT_SUPPORTED) public void methodB() {// 业务逻辑 }
-
如果
methodA
中存在事务,则methodB
会以非事务方式执行,methodA
的事务会被暂停。 -
如果
methodA
中没有事务,则methodB
会以非事务方式执行。
-
6. NEVER
-
描述:以非事务方式执行,如果当前存在事务,则抛出异常。
-
场景:适用于那些绝对不能在事务上下文中执行的场景。
-
例子
@Transactional(propagation = Propagation.REQUIRED) public void methodA() {// 调用methodBmethodB(); }@Transactional(propagation = Propagation.NEVER) public void methodB() {// 业务逻辑 }
-
如果
methodA
中存在事务,则会抛出IllegalTransactionStateException
。 -
如果
methodA
中没有事务,则methodB
会以非事务方式执行。
-
7. NESTED
-
描述:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则开启一个新的事务。
-
场景:适用于需要嵌套事务的场景,例如在复杂的业务流程中。
-
例子
@Transactional(propagation = Propagation.REQUIRED) public void methodA() {// 调用methodBmethodB(); }@Transactional(propagation = Propagation.NESTED) public void methodB() {// 业务逻辑 }
-
如果
methodA
中存在事务,则methodB
会开启一个嵌套事务。 -
如果
methodA
中没有事务,则methodB
会开启一个新的事务。
-
二、事务隔离级别(Isolation Level)
Isolation 隔离
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
事务隔离级别定义了多个事务并发执行时的隔离程度。Spring支持以下几种隔离级别:
1. DEFAULT
-
描述:使用数据库默认的隔离级别(通常是
READ_COMMITTED
)。 -
场景:适用于大多数场景,不需要显式指定隔离级别。
2. READ_UNCOMMITTED
-
描述:允许读取未提交的数据,可能会导致脏读。
-
场景:适用于对数据一致性要求不高的场景。
-
例子
@Transactional(isolation = Isolation.READ_UNCOMMITTED) public void methodA() {// 业务逻辑 }
3. READ_COMMITTED
-
描述:只能读取已提交的数据,避免了脏读,但可能会出现不可重复读。
-
场景:适用于大多数场景,避免了脏读。
-
例子
@Transactional(isolation = Isolation.READ_COMMITTED) public void methodA() {// 业务逻辑 }
4. REPEATABLE_READ
-
描述:保证在同一个事务中,多次读取同一数据的结果是一致的,避免了不可重复读,但可能会出现幻读。
-
场景:适用于对数据一致性要求较高的场景。
-
例子
@Transactional(isolation = Isolation.REPEATABLE_READ) public void methodA() {// 业务逻辑 }
5. SERIALIZABLE
-
描述:最高的隔离级别,事务串行执行,避免了脏读、不可重复读和幻读,但并发性能较差。
-
场景:适用于对数据一致性要求极高的场景,但会牺牲性能。
-
例子
@Transactional(isolation = Isolation.SERIALIZABLE) public void methodA() {// 业务逻辑 }
三、事务回滚规则(Rollback Rules)
Rollback 反转、卷回
@Transactional(rollbackFor = Exception.class)
事务回滚规则定义了在哪些异常情况下事务会被回滚。默认情况下,Spring事务只会在运行时异常(RuntimeException
)时回滚。如果需要在其他类型的异常时也回滚事务,可以在@Transactional
注解中指定rollbackFor
属性。
1. 默认回滚规则
-
描述:默认情况下,Spring事务只会在运行时异常(
RuntimeException
)时回滚。 -
例子
@Transactional public void methodA() {// 业务逻辑throw new RuntimeException("Error occurred"); }
-
如果抛出
RuntimeException
,事务会被回滚。
-
2. 自定义回滚规则
-
描述:可以通过
rollbackFor
属性指定哪些异常会导致事务回滚。 -
例子
@Transactional(rollbackFor = Exception.class) public void methodA() {// 业务逻辑throw new Exception("Error occurred"); }
-
如果抛出
Exception
,事务会被回滚。
-
3. 排除某些异常
-
描述:可以通过
noRollbackFor
属性指定哪些异常不会导致事务回滚。 -
例子:
@