1. 如何使用Spring管理事务
Spring框架在启动的时候会创建相关的bean实例对象,并且会扫描标注有相关注解的类和方法,为这些方法生成代理对象。如果扫描到标注了@Transactional 注解的类或者方法时,会为标注了@Transactional注解的类或者方法生成代理对象,在操作数据库时会通过代理对象来开启事务、提交事务或者回滚事务,无需开发人员关心开启事务、提交、回滚事务的细节
@Transactional(rollbackFor=Exception.class)
public void saveUser(User user){
}
2. 事务分类
Spring 管理的事务可以分为逻辑事务和物理事务两大类
- 逻辑事务:通常指通过Spring等框架管理的事务,这种事务时建立在物理事务之上的,比物理事务更加抽象
- 物理事务:通常指的是针对特定数据库的事务
Spring 支持两种事务声明方式,分别是编程式事务和声明式事务
- 编程式事务:如果系统需要明确的事务,并且需要细粒度的控制各个事务的边界,此时建议使用编程式事务
- 声明式事务:如果系统对于事务控制力度较为粗糙,则建议使用声明式事务,一般通过声明一个事务来控制所有跟声明相关的事务
3. Spring 事务三大接口
Spring 支持事务的管理功能,最核心的就是Spring 事务的三大接口,分别是:PlatformTransactionManager、TransactionDefinition和TransactionStatus。
3.1. PlatformTransactionManager 接口
Spring 并不直接管理事务,而是提供多种事务管理器。Spring将这些事务管理器委托给Hibernate、MyBatis等持久化框架来实现事务管理
PlatformTransactionManager 接口是Spring提供的一个事务管理器接口
package org.springframework.transaction;
import org.springframework.lang.Nullable;
public interface PlatformTransactionManager extends TransactionManager {
/**
* 获取事务状态
*/
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException;
/**
* 提交事务
*/
void commit(TransactionStatus status) throws TransactionException;
/**
* 回滚事务
*/
void rollback(TransactionStatus status) throws TransactionException;
}
3.2. TransactionDefinition 接口
TransactionDefinition接口定义了与事务相关的方法,表示事务属性的常量等信息。部分事务属性的常量与Propagation枚举类中的事务传播类型相对应
package org.springframework.transaction;
public interface TransactionDefinition {
/**
* 支持当前事务,若当前没有事务就创建一个新的事务
*/
int PROPAGATION_REQUIRED = 0;
/**
* 如果当前存在事务,则加入该事务,如果当前没有事务,则以非事务的方式继续运行
*/
int PROPAGATION_SUPPORTS = 1;
/**
* 如果当前存在事务,则加入该事务,如果当前没有事务则抛出异常
*/
int PROPAGATION_MANDATORY = 2;
/**
* 创建一个新事务,如果当前存在事务,则把当前事务挂起
*/
int PROPAGATION_REQUIRES_NEW = 3;
/**
* 以非事务的方式运行,如果当前存在事务,则把当前事务挂起
*/
int PROPAGATION_NOT_SUPPORTED = 4;
/**
* 以非事务的方式运行,如果当前事务存在,则抛出异常
*/
int PROPAGATION_NEVER = 5;
/**
* 如果当前正有一个事务在运行中,则该方法运行在一个嵌套的事务中
* 被嵌套的事务可以独立于封装的事务进行提交或者回滚(这里需要事务的保存点)
* 如果封装的事务不存在,后续事务行为同PROPAGATION_REQUIRES_NEW
*/
int PROPAGATION_NESTED = 6;
/**
* 使用后端数据库默认的隔离级别
*/
int ISOLATION_DEFAULT = -1;
/**
* 最低的隔离级别
*/
int ISOLATION_READ_UNCOMMITTED = 1; // same as java.sql.Connection.TRANSACTION_READ_UNCOMMITTED;
/**
* 防止脏读,但可能残生幻读或不可重复读的问题
*/
int ISOLATION_READ_COMMITTED = 2; // same as java.sql.Connection.TRANSACTION_READ_COMMITTED;
/**
* 可以阻止脏读和不可重复读,但是可能产生幻读
*/
int ISOLATION_REPEATABLE_READ = 4; // same as java.sql.Connection.TRANSACTION_REPEATABLE_READ;
/**
* 可以防止脏读、不可重复读和幻读
*/
int ISOLATION_SERIALIZABLE = 8; // same as java.sql.Connection.TRANSACTION_SERIALIZABLE;
/**
* 使用默认超时时间
*/
int TIMEOUT_DEFAULT = -1;
/**
* 获取事务的传播行为
*/
default int getPropagationBehavior() {
return PROPAGATION_REQUIRED;
}
/**
* 获取事务的隔离级别
*/
default int getIsolationLevel() {
return ISOLATION_DEFAULT;
}
/**
* 获取事务的超时时间
*/
default int getTimeout() {
return TIMEOUT_DEFAULT;
}
/**
* 返回当前是否为只读事务
*/
default boolean isReadOnly() {
return false;
}
/**
* 获取事务名称
*/
@Nullable
default String getName() {
return null;
}
static TransactionDefinition withDefaults() {
return StaticTransactionDefinition.INSTANCE;
}
}
3.3. TransactionStatus 接口
TransactionStatus 接口主要用来存储事务执行失败的状态,并且定义了一组方法,用来判断或者读取事务的状态信息
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
/**
* 是否有保存点
*/
boolean hasSavepoint();
/**
* 将事务设计的数据刷新到磁盘
*/
@Override
void flush();
}
public interface TransactionExecution {
/**
* 判断是否是新事务
*/
boolean isNewTransaction();
/**
* 设置为只读回滚
*/
void setRollbackOnly();
/**
* 是否为只读回滚
*/
boolean isRollbackOnly();
/**
* 判断当前事务是否已经完成
*/
boolean isCompleted();
}
4. Spring事务隔离级别
4.1. ISOLATION_DEFAULT 隔离级别
ISOLATION_DEFAULT 隔离级别是Spring中PlatformTransactionManager默认的事务隔离级别。当Spring 的事务隔离级别设置为ISOLATION_DEF,AULT时,Spring不做事务隔离级别处理,直接会使用数据库默认的事务隔离级别
4.2. ISOLATION_READ_UNCOMMITTED 隔离级别
ISOLATION_READ_COMMITTED 隔离级别是Spring中最低的隔离级别,当Spring 中设置为该隔离级别时,会出现事务A可以读取到事务B未提交的数据。这种隔离级别会产生脏读、不可重复读和幻读的问题。相当于MySQL中的未提交读隔离级别
4.3. ISOLATION_REPEATABLE_READ隔离级别
ISOLATION_REPEATABLE_READ隔离级别可以解决幻读、不可重复读的问题,但是可能会产生幻读的问题。事务A第一次按照条件从数据库中查询出数据后,此时事务B向数据库中插入了符合事务A查询条件的数据,事务A第二次再向数据库查询时,会将事务B插入的数据查询出来,这就是幻读现象。相当于MySQL中的可重复度隔离级别
4.4. ISOLATION_SERIALIZABLE 隔离级别
再ISOLATION_SERIALIZABLE 隔离级别下,事务只能够按照特定的顺序执行,也就是多个事务之间只能够按照串行顺序执行。这是最可靠的隔离级别,然而这种可靠性付出了并发性的代价。相当于MySQL中的串行化隔离级别
5. Spring的7种事务传播类型
5.1. REQUIRED 事务传播类型
REQUIRED 事务传播类型表示如果当前没有事务,就创建一个事务,如果已经存在一个事务,就加入这个事务。这是最常见的事务传播类型,也是Spring当中默认的事务传播类型。
@Transactional(propagation = Propagation.REQUIRED)
5.2. REQUIRED_NEW事务传播类型
REQUIRED_NEW 事务传播类型表示当前存在事务,则把当前事务挂起,并重新创建新的事务执行,直到新的事务提交或者回滚,才会恢复执行原来的事务。这种事务传播类型具备隔离性,将原有事务和新创建的事务隔离,原有事务和新创建的事务的提交和回滚互不影响。新创建的事务和被挂起的事务没有任何关系,他们是两个不相干的独立事务。
@Transactional(propagation = Propagation.REQUIRES_NEW)
5.3. SUPPORTS事务传播类型
SUPPORTS 事务传播类型标识支持当前事务,如果当前没有事务,就以非事务的方式执行.外部不存在事务时,不会开启新的事务,外部存在事务时,将其加入外部事务
@Transactional(propagation = Propagation.SUPPORTS)
5.4. MANDATORY 事务传播类型
MANDATORY 事务传播类型表示支持当前事务,这种事务传播类型具备强制性,当前操作必须存在事务,如果不存在就抛出异常
@Transactional(propagation = Propagation.MANDATORY)
5.5. NOT_SUPPORTED事务传播类型
NOT_SUPPORTED 事务传播类型表示以非事务方式执行,如果当前操作在一个事务中,则把当前事务挂起,直到当前操作完成再恢复事务执行.如果当前操作存在事务,则把事务挂起,以非事务的方式运行
@Transactional(propagation = Propagation.NOT_SUPPORTED)
5.6. NEVER 事务传播类型
NEVER事务传播类型表示以非事务的方式执行,如果当前操作存在事务,则抛出异常.
NEVER事务传播类型和NOT_SUPPORTED事务传播类型的区别在于当前存在事务则NEVERY事务传播类型会抛出异常,而NOT_SUPPORTED事务传播类型会把当前事务挂起,以非事务的方式执行
NEVERY事务传播类型和MANDATORY事务传播类型的区别是NEVERY事务传播类型表示如果当前操作存在事务则抛出异常,而MANDATORY事务传播类型表示如果当前操作不存再事务则抛出异常
@Transactional(propagation = Propagation.NEVER)
5.7. NESTED事务传播类型
NESTED 事务传播类型表示如果当前方法有一个事务正在运行,则这个方法应该运行再一个嵌套事务中,被嵌套的事务可以独立于被封装的事务进行提交或者回滚.如果没有活动事务,则按照REQUIRED 事务传播类型执行
如果封装事务存在,并且外层事务抛出异常回滚,那么内层事务必须回滚.如果内层事务回滚,则并不影响外层事务的提交和回滚.如果封装事务不存在,则按照REQUIRED事务传播类型执行
@Transactional(propagation = Propagation.NESTED)