本文已参与「新人创作礼」活动,一起开启掘金创作之路。
Spring-Tx
ACID
原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)
隔离级别
传播行为
| propagation | 提交 | 异常 | 捕获 | 后异常 |
|---|---|---|---|---|
| required (rollbackOnly) | do nothing / commit | setRollbackOnly / rollback | setRollbackOnly / rollback | |
| required_new (isNewTransaction) | commit / commit | rollback / rollback | rollback / commit | commit / rollback |
| nested (savePoint) | releaseSavepoint / commit | rollbackToSavepoint & releaseSavepoint / rollback | rollbackToSavepoint & releaseSavePoint / commit |
REQUIRED
如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。
本质上是同一个事务
提交:什么也不做
异常传递:rollbackOnly = true
异常捕获:rollbackOnly = true
REQUIRED_NEW
新建事务,如果当前存在事务,把当前事务挂起。
提交:isNewTx = true 提交
异常传递:rollback / rollback
异常捕获:rollback / commit
异常后捕:commit / rollback
NESTED
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
提交:releaseSavepoint / commit
异常传递:rollbackToSavepoint & releaseSavepoint / rollback
异常捕获:rollbackToSavepoint & releaseSavepoint / commit
SUPPORTS
支持当前事务,如果当前没有事务,就以非事务方式执行。
提交:
异常捕获:
异常传递:
MANDATORY
使用当前的事务,如果当前没有事务,就抛出异常。
提交:
异常捕获:
异常传递:
NOT_SUPPORTS
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
提交:
异常捕获:
异常传递:
NEVER
以非事务方式执行,如果当前存在事务,则抛出异常。
提交:
异常捕获:
异常传递:
核心代码-AOP
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(tm,txAttr,joinpointIdentification);
Object retVal = null;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
retVal=invocation.proceedWithInvocation();
} catch(Throwable ex){
// target invocation exception
completeTransactionAfterThrowing(txInfo,ex);
throw ex;
} finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retVal;
回滚事务
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
status.rollbackToHeldSavepoint();
}
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
doRollback(status);
}
else if (status.hasTransaction()) {
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
if (status.isDebug()) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
doSetRollbackOnly(status);
}
else {
if (status.isDebug()) {
logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
}
}
}
提交事务-01
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
if (defStatus.isLocalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Transactional code has requested rollback");
}
processRollback(defStatus);
return;
}
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
}
processRollback(defStatus);
// Throw UnexpectedRollbackException only at outermost transaction boundary
// or if explicitly asked to.
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only");
}
return;
}
processCommit(defStatus);
提交事务-02
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
globalRollbackOnly = status.isGlobalRollbackOnly();
}
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
status.releaseHeldSavepoint();
}
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
doCommit(status);
}
测试代码
package center.leon.springtx;
import center.leon.springtx.service.TxAService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.autoproxy.InfrastructureAdvisorAutoProxyCreator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.event.TransactionalEventListenerFactory;
import org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor;
import org.springframework.transaction.interceptor.TransactionInterceptor;
@Slf4j
@EnableTransactionManagement
@SpringBootApplication
public class SpringTxApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(SpringTxApplication.class, args);
// 基础增强自动代理创建器
final InfrastructureAdvisorAutoProxyCreator infrastructureAdvisorAutoProxyCreator = run.getBean(InfrastructureAdvisorAutoProxyCreator.class);
assert null != infrastructureAdvisorAutoProxyCreator;
// TransactionalEventListener : https://blog.csdn.net/qq_41378597/article/details/105748703
final TransactionalEventListenerFactory transactionalEventListenerFactory = run.getBean(TransactionalEventListenerFactory.class);
assert null != transactionalEventListenerFactory;
//
final AnnotationTransactionAttributeSource annotationTransactionAttributeSource = run.getBean(AnnotationTransactionAttributeSource.class);
assert null != annotationTransactionAttributeSource;
// 事务AOP增强
final TransactionInterceptor transactionInterceptor = run.getBean(TransactionInterceptor.class);
assert null != transactionInterceptor;
//
final BeanFactoryTransactionAttributeSourceAdvisor beanFactoryTransactionAttributeSourceAdvisor = run.getBean(BeanFactoryTransactionAttributeSourceAdvisor.class);
assert null != beanFactoryTransactionAttributeSourceAdvisor;
final TxAService txAService = run.getBean(TxAService.class);
txAService.truncateTable();
/**
*
*/
txAService.required_commit_NoTx_commit();
/**
*
*/
try {
txAService.required_commit_NoTx_Ex();
} catch (Exception e) {
}
/**
* commit / commit
*/
txAService.required_commit_Required_commit();
/**
* setRollbackOnly / rollback
*/
try {
txAService.required_commit_Required_Ex();
} catch (Exception e) {
}
/**
* setRollbackOnly / rollback
*/
try {
txAService.required_commit_Required_Ex_Caught();
} catch (Exception e) {
}
/**
* commit / commit
*/
txAService.required_commit_Required_New_Commit();
/**
* rollback / commit
*/
try {
txAService.required_commit_Required_New_Ex();
} catch (Exception e) {
}
/**
* rollback / commit
*/
txAService.required_commit_Required_New_Ex_Caught();
/**
* commit / rollback
*/
try {
txAService.required_Ex_Required_New_commit();
} catch (Exception e) {
}
/**
* releaseSavePoint / commit
*/
txAService.required_commit_Required_Nested_Commit();
/**
* rollbackToSavePoint & releaseSavePoint / rollback
*/
try {
txAService.required_commit_Required_Nested_Ex();
} catch (Exception e) {
}
/**
* rollbackToSavePoint & releaseSavePoint / commit
*/
txAService.required_commit_Required_Nested_Ex_Caught();
/**
* THIS 以下
*/
txAService.required_commit_or_ex_catch_This_Any_propagation_commit_or_ex_or_caught_is_invalid();
/**
* Spring 以下
*/
txAService.required_commit_or_ex_catch_Spring_Any_propagation_commit_or_ex_or_caught_invalid();
System.out.println("天地英雄气,千秋尚凛然");
}
}
ServiceA Interface
package center.leon.springtx.service;
/**
* Description:
*
* @author Leon
* @since 2022/1/11 1:57 下午
*/
public interface TxAService {
void truncateTable();
void required_commit_NoTx_commit();
void required_commit_NoTx_Ex();
void required_commit_Required_commit();
void required_commit_Required_Ex();
void required_commit_Required_Ex_Caught();
void required_commit_Required_New_Commit();
void required_commit_Required_New_Ex();
void required_commit_Required_New_Ex_Caught();
void required_Ex_Required_New_commit();
void required_commit_Required_Nested_Commit();
void required_commit_Required_Nested_Ex();
void required_commit_Required_Nested_Ex_Caught();
// this 以下是
void required_commit_or_ex_catch_This_Any_propagation_commit_or_ex_or_caught_is_invalid();
void required_commit_or_ex_catch_This_Any_propagation_commit_or_ex_or_caught_is_invalid_this(String name);
// Spring 以下是
void required_commit_or_ex_catch_Spring_Any_propagation_commit_or_ex_or_caught_invalid();
void required_commit_or_ex_catch_Spring_Any_propagation_commit_or_ex_or_caught_invalid_Spring(String name);
}
ServiceAImpl Class
package center.leon.springtx.service.impl;
import center.leon.springtx.dao.MonkeyDao;
import center.leon.springtx.mapper.MonkeyMapper;
import center.leon.springtx.service.TxAService;
import center.leon.springtx.service.TxBService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* Description:
*
* @author Leon
* @since 2022/1/11 1:57 下午
*/
@Service
@Slf4j
public class TxAServiceImpl implements TxAService {
@Autowired
private TxBService txBService;
@Autowired
private MonkeyDao monkeyDao;
@Autowired
private MonkeyMapper monkeyMapper;
@Autowired
private TxAService txAService;
@Override
public void truncateTable() {
monkeyMapper.truncateTable();
}
/**
* A 开启事务 ts(isNewTx = true)
* B TransactionInterceptor 不拦截,无事务增强
*/
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void required_commit_NoTx_commit() {
monkeyDao.insert("required_commit_NoTx_commit_A");
txBService.required_commit_NoTx_commit("required_commit_NoTx_commit_B");
}
/**
* A 开启事务 ts(isNewTx = true)
* B TransactionInterceptor 不拦截,无事务增强
* B 抛异常,捕获异常,向上传递。
* 在 A catch 处理程序中 根据 rollbackFor 配置决定是否回滚或提交
* 回滚:A ts(isNewTx = true) 回滚。继续抛出 异常。
*/
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void required_commit_NoTx_Ex() {
monkeyDao.insert("required_commit_NoTx_Ex_A");
txBService.required_commit_NoTx_Ex("required_commit_NoTx_Ex_B");
}
/**
* A 开启事务 ts(isNewTx = true)(线程不带connect)
* B 开启事务 ts(isNewTx = false)
* B 提交事务 ts(isNewTx = false),不 commit
* A 开启事务 ts(isNewTx = true),commit
*/
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void required_commit_Required_commit() {
monkeyDao.insert("required_commit_Required_commit_A");
txBService.required_commit_Required_commit("required_commit_Required_commit_B");
}
/**
* A 开启事务 ts(isNewTx = true)(线程不带connect)
* B 开启事务 ts(isNewTx = false)
* B 抛异常
* 在 B catch 处理程序中 根据 rollbackFor 配置决定是否回滚或提交
* 回滚:B ts(isNewTx = false, ConnectionHolder::rollbackOnly = true), 继续抛出异常
* 提交:
* 在 A catch 处理程序中 根据 rollbackFor 配置决定是否回滚或提交
* 回滚:A ts(isNewTx = true, ConnectionHolder::rollbackOnly = true), 执行回滚逻辑。继续抛出异常
* 提交:
*/
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void required_commit_Required_Ex() {
monkeyDao.insert("required_commit_Required_Ex_A");
txBService.required_commit_Required_Ex("required_commit_Required_Ex_B");
}
/**
* A 开启事务 ts(isNewTx = true)(线程不带connect)
* B 开启事务 ts(isNewTx = false)
* B 抛异常
* 在 B catch 处理程序中 根据 rollbackFor 配置决定是否回滚或提交
* 回滚:B ts(isNewTx = false, ConnectionHolder::rollbackOnly = true), 继续抛出异常
* 提交:
* 在 A catch 处理程序中 根据 rollbackFor 配置决定是否回滚或提交
* 回滚:A ts(isNewTx = true, ConnectionHolder::rollbackOnly = true), 执行回滚逻辑。继续抛出异常
* 提交:
*/
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void required_commit_Required_Ex_Caught() {
monkeyDao.insert("required_commit_Required_Ex_Caught_A");
try {
txBService.required_commit_Required_Ex_Caught("required_commit_Required_Ex_Caught_B");
} catch (Exception e) {
log.info("required_commit_Required_Ex_Caught_B. error : {}", e.getMessage(), e);
}
}
/**
* A 开启事务 ts(isNewTx = true)(线程不带connect)
* B 开启事务 ts(isNewTx = false)(线程挂起资源时,清除connect。重新获取ts(isNewTx = true),开启事务,重新获取connect)
* 提交:B con.commit();
* 提交:A con.commit();
*/
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void required_commit_Required_New_Commit() {
monkeyDao.insert("required_commit_Required_New_Commit_A");
txBService.required_commit_Required_New_Commit("required_commit_Required_New_Commit_B");
}
/**
* A 开启事务 ts(isNewTx = true)(线程不带connect)
* B 开启事务 ts(isNewTx = false)(线程挂起资源时,清除connect。重新获取ts(isNewTx = true),开启事务,重新获取connect)
* 回滚:B con.rollback(); 继续向上传递异常
* 回滚:A con.rollback(); 继续向上传递异常
*/
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void required_commit_Required_New_Ex() {
monkeyDao.insert("required_commit_Required_New_Ex_A");
txBService.required_commit_Required_New_Ex("required_commit_Required_New_Ex_B");
}
/**
* A 开启事务 ts(isNewTx = true)(线程不带connect)
* B 开启事务 ts(isNewTx = false)(线程挂起资源时,清除connect。重新获取ts(isNewTx = true),开启事务,重新获取connect)
* 提交:B con.commit();
* 回滚:A con.rollback(); 继续向上传递异常
*/
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void required_commit_Required_New_Ex_Caught() {
monkeyDao.insert("required_commit_Required_New_Ex_Caught_A");
try {
txBService.required_commit_Required_New_Ex_Caught("required_commit_Required_New_Ex_Caught_B");
} catch (Exception e) {
log.info("required_commit_Required_New_Ex_Caught_B : {}", e.getMessage(), e);
}
}
/**
* A 开启事务 ts(isNewTx = true)(线程不带connect)
* B 开启事务 ts(isNewTx = false)(线程挂起资源时,清除connect。重新获取ts(isNewTx = true),开启事务,重新获取connect)
* 提交:B con.commit();
* 回滚:A con.rollback(); 继续向上传递异常
*/
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void required_Ex_Required_New_commit() {
monkeyDao.insert("required_Ex_Required_New_commit_A");
txBService.required_Ex_Required_New_commit("required_Ex_Required_New_commit_B");
int i = 0 / 0;
}
/**
* A 开启事务 ts(isNewTx = true)(线程不带connect)
* B 开启事务 ts(isNewTx = false, crateAndHoledSavePoint())
* 提交:B release savePoint
* 提交:A con.commit();
*/
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void required_commit_Required_Nested_Commit() {
monkeyDao.insert("required_commit_Required_Nested_Commit_A");
txBService.required_commit_Required_Nested_Commit("required_commit_Required_Nested_Commit_B");
}
/**
* A 开启事务 ts(isNewTx = true)(线程不带connect)
* B 开启事务 ts(isNewTx = false, crateAndHeldSavePoint())
* 回滚:B rollbackTo/release savePoint 继续向上传递异常
* 回滚:A con.rollback();
*/
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void required_commit_Required_Nested_Ex() {
monkeyDao.insert("required_commit_Required_Nested_Ex_A");
txBService.required_commit_Required_Nested_Ex("required_commit_Required_Nested_Ex_B");
}
/**
* A 开启事务 ts(isNewTx = true)(线程不带connect)
* B 开启事务 ts(isNewTx = false, crateAndHeldSavePoint())
* 回滚:B rollbackTo/release savePoint (ConnectionHolder:rollbackOnly still is false) 继续向上传递异常
* A catch B ex
* 提交:A con.rollback();
*/
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void required_commit_Required_Nested_Ex_Caught() {
monkeyDao.insert("required_commit_Required_Nested_Ex_Caught_A");
try {
txBService.required_commit_Required_Nested_Ex_Caught("required_commit_Required_Nested_Ex_Caught_B");
} catch (Exception e) {
log.info("required_commit_Required_Nested_Ex_Caught_B. error : {}", e.getMessage(), e);
}
}
/**
* A 开启事务 ts(isNewTx = true)(线程不带connect)
* this.方法名调用是对象内部方法调用,不会通过Spring代理,不会被TransactionInterceptor事务拦截器增强,也就是事务不会起作用。
*/
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void required_commit_or_ex_catch_This_Any_propagation_commit_or_ex_or_caught_is_invalid() {
monkeyDao.insert("required_commit_or_ex_catch_This_Any_propagation_commit_or_ex_or_caught_is_invalid_A");
this.required_commit_or_ex_catch_This_Any_propagation_commit_or_ex_or_caught_is_invalid_this("required_commit_or_ex_catch_This_Any_propagation_commit_or_ex_or_caught_is_invalid_this");
}
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.NEVER)
public void required_commit_or_ex_catch_This_Any_propagation_commit_or_ex_or_caught_is_invalid_this(String name) {
monkeyDao.insert(name);
}
/**
* A 开启事务 ts(isNewTx = true)(线程不带connect)
* Spring 开启事务 ts(isNewTx = false)
*/
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void required_commit_or_ex_catch_Spring_Any_propagation_commit_or_ex_or_caught_invalid() {
monkeyDao.insert("required_commit_or_ex_catch_Spring_Any_propagation_commit_or_ex_or_caught_invalid_A");
txAService.required_commit_or_ex_catch_Spring_Any_propagation_commit_or_ex_or_caught_invalid_Spring("required_commit_or_ex_catch_Spring_Any_propagation_commit_or_ex_or_caught_invalid_Spring");
}
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.NEVER)
public void required_commit_or_ex_catch_Spring_Any_propagation_commit_or_ex_or_caught_invalid_Spring(String name) {
monkeyDao.insert(name);
}
}
ServiceB Interface
package center.leon.springtx.service;
/**
* Description:
*
* @author Leon
* @since 2022/1/11 1:57 下午
*/
public interface TxBService {
void required_commit_NoTx_commit(String name);
void required_commit_NoTx_Ex(String name);
void required_commit_Required_commit(String name);
void required_commit_Required_Ex(String name);
void required_commit_Required_Ex_Caught(String name);
void required_commit_Required_New_Commit(String name);
void required_commit_Required_New_Ex(String name);
void required_commit_Required_New_Ex_Caught(String name);
void required_Ex_Required_New_commit(String name);
void required_commit_Required_Nested_Commit(String name);
void required_commit_Required_Nested_Ex(String name);
void required_commit_Required_Nested_Ex_Caught(String name);
}
ServiceBImpl Class
package center.leon.springtx.service.impl;
import center.leon.springtx.dao.MonkeyDao;
import center.leon.springtx.service.TxBService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* Description:
*
* @author Leon
* @since 2022/1/11 1:57 下午
*/
@Service
public class TxBServiceImpl implements TxBService {
@Autowired
private MonkeyDao monkeyDao;
@Override
public void required_commit_NoTx_commit(String name) {
monkeyDao.insert(name);
}
@Override
public void required_commit_NoTx_Ex(String name) {
monkeyDao.insert(name);
int i = 0 / 0;
}
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void required_commit_Required_commit(String name) {
monkeyDao.insert(name);
}
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void required_commit_Required_Ex(String name) {
monkeyDao.insert(name);
int i = 0 / 0;
}
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void required_commit_Required_Ex_Caught(String name) {
monkeyDao.insert(name);
int i = 0 / 0;
}
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void required_commit_Required_New_Commit(String name) {
monkeyDao.insert(name);
}
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void required_commit_Required_New_Ex(String name) {
monkeyDao.insert(name);
int i = 0 / 0;
}
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void required_commit_Required_New_Ex_Caught(String name) {
monkeyDao.insert(name);
int i = 0 / 0;
}
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void required_Ex_Required_New_commit(String name) {
monkeyDao.insert(name);
}
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.NESTED)
public void required_commit_Required_Nested_Commit(String name) {
monkeyDao.insert(name);
}
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.NESTED)
public void required_commit_Required_Nested_Ex(String name) {
monkeyDao.insert(name);
int i = 0 / 0;
}
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.NESTED)
public void required_commit_Required_Nested_Ex_Caught(String name) {
monkeyDao.insert(name);
int i = 0 / 0;
}
}