SpringTx的使用和简单功能原理
基本操作
-
判断哪些动作是事务
-
参与已存在的或创建新的事务
-
完成
-
提交
-
回滚
-
透过代码看SQL
初始化连接线程时执行
/* mysql-connector-java-8.0.19 (Revision: a0ca826f5cdf51a98356fdfb1bf251eb042f80bf) */
SELECT @@session.auto_increment_increment AS auto_increment_increment, @@character_set_client AS character_set_client, @@character_set_connection AS character_set_connection, @@character_set_results AS character_set_results, @@character_set_server AS character_set_server, @@collation_server AS collation_server, @@collation_connection AS collation_connection, @@init_connect AS init_connect, @@interactive_timeout AS interactive_timeout, @@license AS license, @@lower_case_table_names AS lower_case_table_names, @@max_allowed_packet AS max_allowed_packet, @@net_write_timeout AS net_write_timeout, @@performance_schema AS performance_schema, @@query_cache_size AS query_cache_size, @@query_cache_type AS query_cache_type, @@sql_mode AS sql_mode, @@system_time_zone AS system_time_zone, @@time_zone AS time_zone, @@tx_isolation AS transaction_isolation, @@wait_timeout AS wait_timeout
SET NAMES utf8mb4
SET character_set_results = NULL
SET autocommit=1
SET sql_mode='NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES'
SELECT @@session.tx_read_only
SELECT @@session.tx_isolation
执行带有事务的业务
SET autocommit=0 --关闭自动提交事务
insert into tx (tx_name) values ('random1651040479277') --从第一条语句执行时开启事务
select tx0_.id as id1_0_, tx0_.tx_name as tx_name2_0_ from tx tx0_ --平平无奇的查询语句
commit --提交事务
SET autocommit=1 --开启自动提交事务
执行非默认隔离级别的业务
SELECT @@session.tx_isolation --查询当前会话的隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED --不一样就设置
SET autocommit=0
insert into tx (tx_name) values ('random1651053787832')
select tx0_.id as id1_0_, tx0_.tx_name as tx_name2_0_ from tx tx0_
commit
SET autocommit=1
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ --执行后再改回去
设计的巧妙点:springtx并非采用我们熟知的begin或start transaction开启事务。而是使用autocommit=0来隐式开启事务。
事务的开启和关闭:
- 显式启动事务语句, begin 或 start transaction。配套的提交语句是 commit,回滚语句是 rollback。
- set autocommit=0,这个命令会将这个线程的自动提交关掉。意味着如果你只执行一个 select 语句,这个事务就启动了,而且并不会自动提交。这个事务持续存在直到你主动执行 commit 或 rollback 语句,或者断开连接。
- set autocommit=1,如果执行 commit work and chain,则是提交事务并自动启动下一个事务,这样也省去了再次执行 begin 语句的开销。
源码
声明式事务@Transactional
public @interface Transactional {
@AliasFor("transactionManager")
String value() default "";
@AliasFor("value")
String transactionManager() default "";
Propagation propagation() default Propagation.REQUIRED; // 传播行为
Isolation isolation() default Isolation.DEFAULT; // 隔离级别
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT; // 超时机制,默认不超时
boolean readOnly() default false; // 是否只读
Class<? extends Throwable>[] rollbackFor() default {};
String[] rollbackForClassName() default {};
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
}
字段解读
-
value & transactionManager
指定使用某个事务管理器
TransactionManager,比如使用spring-data-jpa时默认是JpaTransactionManager -
propagation
事务的传播表现
可选项
-
REQUIRED,默认,当前有事务则加入,没有则新建一个
- 如果当前不存在事务则:
@Override public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException { // ... 省略 else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED || def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW || def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { // 挂起当前事务 SuspendedResourcesHolder suspendedResources = suspend(null); if (debugEnabled) { logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def); } try { // 开启一个新的事务 return startTransaction(def, transaction, debugEnabled, suspendedResources); } catch (RuntimeException | Error ex) { resume(null, suspendedResources); throw ex; } } }- 如果当前存在事务则:
private TransactionStatus handleExistingTransaction( TransactionDefinition definition, Object transaction, boolean debugEnabled) throws TransactionException { // ... 省略 // 如果能走到这步说明是REQUIRED或者SUPPORTS,那么 // Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED. // ... 省略 boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); // 第二个参数表示当前事务,第三个参数false表示不开启事务 return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null); } -
REQUIRES_NEW,不管是否存在事务,都创建一个新的事务,原来的挂起(Suspend),新的执行完毕,继续执行老的事务
-
如果当前不存在事务,同REQUIRED
-
如果当前存在事务则:
/** * Create a TransactionStatus for an existing transaction. */ private TransactionStatus handleExistingTransaction( TransactionDefinition definition, Object transaction, boolean debugEnabled) throws TransactionException { // ... if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) { // 挂起当前事务 SuspendedResourcesHolder suspendedResources = suspend(transaction); try { // 开启新的事务 return startTransaction(definition, transaction, debugEnabled, suspendedResources); } catch (RuntimeException | Error beginEx) { // ... } } } -
-
NESTED,如果当前存在事务,则运行在一个嵌套的事务中,如果当前没有事务,则按照REQUIRED属性执行。某些DAO层框架不支持,比如JPA。
- 如果当前不存在事务,同REQUIRED
- 如果当前存在事务则:
/** * Create a TransactionStatus for an existing transaction. */ private TransactionStatus handleExistingTransaction( TransactionDefinition definition, Object transaction, boolean debugEnabled) throws TransactionException { // ... if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { if (!isNestedTransactionAllowed()) { throw new NestedTransactionNotSupportedException( "Transaction manager does not allow nested transactions by default - " + "specify 'nestedTransactionAllowed' property with value 'true'"); } // 是否使用保存点来支持嵌套事务 if (useSavepointForNestedTransaction()) { // 使用jdbc3.0的保存点实现 // Create savepoint within existing Spring-managed transaction, // through the SavepointManager API implemented by TransactionStatus. // Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization. DefaultTransactionStatus status = prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null); status.createAndHoldSavepoint(); return status; } else { // Nested transaction through nested begin and commit/rollback calls. // Usually only for JTA: Spring synchronization might get activated here // in case of a pre-existing JTA transaction. return startTransaction(definition, transaction, debugEnabled, null); } } } -
SUPPORTS,若当前存在事务,则在事务中运行,否则以非事务形式运行
private TransactionStatus handleExistingTransaction( TransactionDefinition definition, Object transaction, boolean debugEnabled) throws TransactionException { // ... 省略 // 如果能走到这步说明是REQUIRED或者SUPPORTS,那么 // Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED. // ... 省略 boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); // 第二个参数表示当前事务,第三个参数false表示不开启事务 return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null); } -
MANDATORY,必须在一个已有的事务中执行,否则抛出异常
@Override public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException { // ... 省略 // 如果存在事务则走这里后return if (isExistingTransaction(transaction)) { // Existing transaction found -> check propagation behavior to find out how to behave. return handleExistingTransaction(def, transaction, debugEnabled); } // 如果不存在事务走到了这里,但传播行为又是MANDATORY则抛异常 // No existing transaction found -> check propagation behavior to find out how to proceed. if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) { throw new IllegalTransactionStateException( "No existing transaction found for transaction marked with propagation 'mandatory'"); } } -
NOT_SUPPORTED,这个方法不开启事务
private TransactionStatus handleExistingTransaction( TransactionDefinition definition, Object transaction, boolean debugEnabled) throws TransactionException { // ... 省略 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) { if (debugEnabled) { logger.debug("Suspending current transaction"); } // 如果现在有事务则挂起当前事务 Object suspendedResources = suspend(transaction); // 是否起新的同步环境(其实TransactionSynchronizationManager就是一堆ThreadLocal做线程间数据同步) boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); // 第三个入参newTransaction=false 表示不开启事务 return prepareTransactionStatus( definition, null, false, newSynchronization, debugEnabled, suspendedResources); } } -
NEVER,不允许在事务内执行
private TransactionStatus handleExistingTransaction( TransactionDefinition definition, Object transaction, boolean debugEnabled) throws TransactionException { if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) { throw new IllegalTransactionStateException( "Existing transaction found for transaction marked with propagation 'never'"); } }
-
-
isolation
事务的隔离级别
可选项
- DEFAULT,默认,与数据库连接时读取数据库默认值
SELECT @@session.tx_isolationorSELECT @@session.transaction_isolation - READ_UNCOMMITTED,读未提交
- READ_COMMITTED,读已提交
- REPEATABLE_READ,可重复度
- SERIALIZABLE,串行化
实现原理:如果业务想要的隔离级别与初始化时不一致则执行SQL设置
SELECT @@session.tx_isolation --查询当前会话的隔离级别 SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED --不一样就设置 ... SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ --执行后再改回去 - DEFAULT,默认,与数据库连接时读取数据库默认值
-
timeout
事务超时时间,默认不超时(-1)。
实现原理:将超时时间参数传递给DAO层框架,由DAO层框架自己实现判断,于执行SQL语句前判断事务是否超时。
-
readOnly
是否是只读事务,如果是,但是又有增删改的话则抛出异常
java.sql.SQLException: Connection is read-only实现原理:在事务执行前后执行SQL设置
set session transaction read only ... set session transaction read write
事务事件监听@TransactionalEventListener
支持提交前,提交后,回滚后,完成后(提交or回滚)
使用场景
案例1:事务提交后再做某种操作,比如发送MQ
案例2:事务回滚后对某些业务进行补偿
使用方法
@Slf4j
@Service
public class TransactionalTestService {
@Autowired
private ApplicationEventPublisher eventPublisher;
@Transactional
public void propWithListener() {
System.out.println("start tx");
eventPublisher.publishEvent(new TxAfterEvent("tx after event"));
System.out.println("end tx");
}
@TransactionalEventListener
public void listenEvent(TxAfterEvent txAfterEvent) {
System.out.println(txAfterEvent);
}
public static class TxAfterEvent extends ApplicationEvent {
public TxAfterEvent(Object source) {
super(source);
}
}
}
结果
start tx
end tx
cn.cccyq.jpademo.service.TransactionalTestService$TxAfterEvent[source=tx after event]
事务管理器类图
基本事务管理类PlatformTransactionManager
public interface PlatformTransactionManager extends TransactionManager {
/**
* 获取事务的状态信息(依据传播行为的不同,可能返回一个已激活的事务或创建一个新的独立事务)
*/
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
/**
* 提交当前事务,依据事务的当前状态,也可能会进行回滚,比如标记为rollback-only的事务
*/
void commit(TransactionStatus status) throws TransactionException;
/**
* 回滚当前事务,依据传播行为的不同,非独立的内部事务仅会打上rollback-only标记
*/
void rollback(TransactionStatus status) throws TransactionException;
}
AOP拦截类TransactionInterceptor
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
}
AOP处理类TransactionAspectSupport
public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean {
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
}
}
事务定义类TransactionDefinition
用以定义事务的基本属性
事务数据共享类TransactionSynchronizationManager
用以实现同一个事务在不同类不同方法之间共享数据(其实就是一堆ThreadLocal)
参考
- SpringTx源码解析(一),anyoptional,anyoptional.com/2021/04/16/…
- SpringTx源码解析(二),anyoptional,www.jianshu.com/p/842d73955…
- Spring事务超时探讨,blog.csdn.net/m0_60449641…
- Spring事务注解@Transactional原理解析,zhuanlan.zhihu.com/p/141102728
- Spring 事务扩展机制 TransactionSynchronization,www.cnblogs.com/kkkfff/p/13…