精华内容
下载资源
问答
  • 阅读文本大概需要3分钟Spring管理的事务是逻辑事务,而且物理事务和逻辑事务最大差别就在于事务传播行为,事务传播行为用于指定在多个事务方法间调用时,事务是如何在这些方法间传播的。下面以a.save()里调用了b....

    阅读文本大概需要3分钟

    Spring管理的事务是逻辑事务,而且物理事务和逻辑事务最大差别就在于事务传播行为,事务传播行为用于指定在多个事务方法间调用时,事务是如何在这些方法间传播的。

    下面以a.save()里调用了b.save()举例,事务方法之间调用时事务如何传播。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @Service
    public class A {
    @Autowired
    private B b;
    @Transactional(propagation=Propagation.XXX)
    public void save() {
    // ...
    b.save();
    // ...
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    @Service
    public class B {
    @Transactional(propagation=Propagation.XXX)
    public void save() {
    // ...
    }
    }

    1. REQUIRED

    默认的spring事务传播级别,使用该级别的特点是,如果上下文中已经存在事务,那么就加入到事务中执行,如果当前上下文中不存在事务,则新建事务执行。如下图:92c601ae7220f372f4987d3dca515a93.png由于两个方法属于同一个物理事务,如果发生回滚,则两者都回滚。

    2. SUPPORTS

    顾名思义就是可以支持事务,如果b.save()在事务环境中运行,则以事务形式运行,否则以非事务运行。
    注:
    这里所谓的非事务形式的数据库访问只是指没有显式的事务边界而已,就是说数据库操作只是 auto-commit 的方式,在数据库的物理事务概念上,还是有事务的。譬如在这里,a.save()调用b.save(),如果当a是非事务执行(非手动提交事务,而是auto-commit),那么b.save()在执行前,a.save()的物理事务就要先提交,而同时,b.save()的物理事务也是auto-commit,这样才是这里说的b.save()以非事务方法运行,而不是指b.save()不开启数据库物理事务。而所谓的b.save()在事务环境中运行,是指当b的外层a.save()本身是手动提交事务时,b.save()也会包含在a.save()里边的同一个事务去执行,也就是说a.save()与b.save()的SQL操作在同一个物理事务中。

    75548f54d1f49b2369da3871dafe0584.png

    3. MANDATORY

    必须在一个事务中运行,也就是说,b.save()只能在已有事务的方法中被调用,否则会抛异常。

    f074fac50fdb487364d5bc66359b9cbd.png

    4. REQUIRES_NEW

    总是会创建一个新事务(包括物理事务),该传播级别的特点是,每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。如下图:

    3880d7a85e31771538716865f604bdda.png

    两个方法之间既不属于同一个逻辑事务也不属于同一个物理事务。

    5. NOT_SUPPORTED

    顾名思义不支持事务,当处于存在事务的上下文环境中运行时,b.save()会暂停当前已开启的事务,意味着a.save()的事务被挂起直至b.save()以非事务方法运行完毕后,a.save()的事务继续执行。

    1b4afa87b319dd9bcfb8d449634ebdee.png

    6. NEVER

    绝不能在事务环境中运行,如果a.save()里声明了使用事务,而b.save()的事务类型声明为never,那么只能以抛异常告终。

    7caa5c4870b6abf4b6bc11c870d94326.png

    与Mandatory相反,Mandatory意思是强制要求上下文中有事务(外层有事务),否则抛异常,而Never是上下文中不能有事务(外层无事务),否则抛异常。

    7. NESTED

    嵌套事务支持。该传播级别特征是,如果上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。

    f03ee790e5d0390d1f19ddacebd70065.png

    Nested和RequiresNew的区别:

    • RequiresNew每次都创建新的独立的物理事务,而Nested只有一个物理事务;Nested嵌套事务回滚或提交不会导致外部事务回滚或提交,但外部事务回滚将导致嵌套事务回滚,而RequiresNew由于都是全新的事务,所以之间是无关联的

    • Nested使用JDBC 3的保存点实现,即如果使用低版本驱动将导致不支持嵌套事务

      使用嵌套事务,必须确保具体事务管理器实现的nestedTransactionAllowed属性为true,否则不支持嵌套事务,如DataSourceTransactionManager默认支持,而HibernateTransactionManager默认不支持,需要我们来开启。

    https://fgu123.github.io/2019/03/19/Spring-Transaction-Propagation/

    往期精彩

    01 漫谈发版哪些事,好课程推荐

    02 Linux的常用最危险的命令

    03 精讲Spring Boot—入门+进阶+实例

    04 优秀的Java程序员必须了解的GC哪些

    05 互联网支付系统整体架构详解

    关注我

    每天进步一点点

    c41f3a3c3f7700e469805e436be06767.png

    喜欢!在看☟
    展开全文
  • TransactionInterceptor它是用来执行事务功能的,它是一个方法拦截器,如下所示:它实现了 MethodInterceptor 接口,而该接口只有一个 invoke 方法,用来执行目标方法public Object invoke(MethodInvocation ...

    TransactionInterceptor

    它是用来执行事务功能的,它是一个方法拦截器,如下所示:

    df17d04268d2f1af076f5f5a66be5946.png

    它实现了 MethodInterceptor 接口,而该接口只有一个 invoke 方法,用来执行目标方法

    public Object invoke(MethodInvocation invocation) throws Throwable {
    	Class<?> targetClass = (invocation.getThis() != null ?AopUtils.getTargetClass(invocation.getThis()) : null);
        // 调用父类的方法
    	return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
    }

    父类的 invokeWithinTransaction 方法定义了一个事务方法执行的框架,而每一步再细分为方法进行实现,代码如下:

    protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation){
    	// 1. 获取事务属性
    	TransactionAttributeSource tas = getTransactionAttributeSource();
    	final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
    	// 2. 获取事务管理器
    	final PlatformTransactionManager tm = determineTransactionManager(txAttr);
    	// 3. 获取需要事务的方法名称:类目.方法名
    	final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
    	// 4. 声明式事务
    	if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
    		// 5. 获取该方法上事务的信息
    		TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
    		Object retVal = null;
    		try {
    			// 6. 目标方法执行,它是一个拦截器链
    			retVal = invocation.proceedWithInvocation();
    		}
    		catch (Throwable ex) {
    			// 7. 事务回滚
    			completeTransactionAfterThrowing(txInfo, ex);
    			throw ex;
    		}
    		finally {
    			// 8. 清除事务信息
    			cleanupTransactionInfo(txInfo);
    		}
    		// 9. 事务提交
    		commitTransactionAfterReturning(txInfo);
    		return retVal;
    	}
    	else {
    		// 10. 编程式事务,流程和声明式事务一致
    	}
    }

    一个事务方法执行流程大概有以下几个步骤:

    1. 获取事务属性
    2. 获取事务管理器
    3. 获取需要事务的方法名称
    5. 获取该方法上事务的信息
    6. 目标方法执行
    7. 事务回滚
    8. 清除事务信息
    9. 事务提交

    获取事务属性

    首先去获取方法上面 Translational 注解的属性,在 Spring 事务初始化源码分析 中已经分析过了,即在 AnnotationTransactionAttributeSource.computeTransactionAttribute 中进行获取。

    获取事务管理器

    每个事务都由对应的事务管理器,所以在事务开始钱需要获取对应的事务管理器

    protected PlatformTransactionManager determineTransactionManager(TransactionAttribute txAttr) {
    	if (txAttr == null || this.beanFactory == null) {
    		return getTransactionManager();
    	}
    	// 事务管理器名称
    	String qualifier = txAttr.getQualifier();
    	if (StringUtils.hasText(qualifier)) {
    		return determineQualifiedTransactionManager(this.beanFactory, qualifier);
    	}
    	else if (StringUtils.hasText(this.transactionManagerBeanName)) {
    		return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);
    	}
    	else {
    		// 默认事务管理器
    		PlatformTransactionManager defaultTransactionManager = getTransactionManager();
    		defaultTransactionManager = this.beanFactory.getBean(PlatformTransactionManager.class);
    		// .....
    		return defaultTransactionManager;
    	}
    }

    获取需要事务的方法名称

    这里主要去获取名称的名称,为 全限定类名+方法名的方式:method.getDeclaringClass().getName() + '.' + method.getName();

    获取方法上事务的信息

    该部分是 Spring 事务最复杂的部分,比如说去创建一个事务,设置事务的隔离级别,超时时间,对事务传播方式的处理,事务的挂起和恢复等;事务信息 TransactionInfo 包含了目标方法执行前的所有状态信息,如果方法执行失败,则会根据该信息来进行回滚。

    对应方法为:

    TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

    代码如下所示:

    创建事务

    protected TransactionInfo createTransactionIfNecessary(PlatformTransactionManager tm,
    		TransactionAttribute txAttr, final String joinpointIdentification) {
    	// 设置事务的名称,为方法全限定名joinpointIdentification
    	if (txAttr != null && txAttr.getName() == null) {
    		txAttr = new DelegatingTransactionAttribute(txAttr) {
    			public String getName() {
    				return joinpointIdentification;
    			}
    		};
    	}
    	TransactionStatus status = null;
    	if (txAttr != null) {
    		if (tm != null) {
    		    // 获取事务
    			status = tm.getTransaction(txAttr);
    		}
    	}
    	// 创建事务信息
    	return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
    }

    获取事务

    在方法 getTransaction 中获取事务,是最为复杂的逻辑,在其中处理隔离级别,超时时间和传播方式等。

    public final TransactionStatus getTransaction(TransactionDefinition definition){
    	// 获取事务
    	Object transaction = doGetTransaction();
        // ...
    	// 如果已经存在事务了,则处理事务的传播方式,如挂起存在的事务,新建事务等
    	if (isExistingTransaction(transaction)) {
    		return handleExistingTransaction(definition, transaction, debugEnabled);
    	}
    	// .....
    	
    	// 如果不存在事务,且事务的传播方式为 mandatory, 则抛出异常
    	if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
    		throw new IllegalTransactionStateException("....");
    	}
    	else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
    			definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
    			definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
    		SuspendedResourcesHolder suspendedResources = suspend(null);
    		// 如果事务的传播方式为 requested, requestes_new,nested,则会新建一个事务
    		try {
    			boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
    			// 第三个参数为true表示新建事务
    			DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
    			// 构造 transaction,包括隔离级别,timeout,如果是新连接,则绑定到当前线程
    			doBegin(transaction, definition);
    			// 同步新事务
    			prepareSynchronization(status, definition);
    			return status;
    		}
    		catch (RuntimeException | Error ex) {
    			resume(null, suspendedResources);
    			throw ex;
    		}
    	}
    	else {
    		boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
    		return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
    	}
    }

    获取事务 doGetTransaction(),在该方法中,会根据 DataSource 获取一个连接,如下:

    protected Object doGetTransaction() {
    	DataSourceTransactionObject txObject = new DataSourceTransactionObject();
        //如果设置了允许嵌套事务,则开启保存点;只有嵌套事务才有保存点
    	txObject.setSavepointAllowed(isNestedTransactionAllowed());
        // 根据 DataSource 获取连接,ConnectionHolder为一个数据库连接
    	ConnectionHolder conHolder = TransactionSynchronizationManager.getResource(obtainDataSource());
    	txObject.setConnectionHolder(conHolder, false);
    	return txObject;
    }

    之后,判断当前线程是否存在事务,如果存在事务,则根据事务的传播方式来处理已存在的事务,这里先不看。

    如果不存在事务且事务的传播方式为 requested, requestes_new,nested,则会新建一个事务:

    DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
    //definition事务属性
    //transaction事务
    //newTransaction是否事务新事务
    //suspendedResources需要挂起的事务
    protected DefaultTransactionStatus newTransactionStatus(
    		TransactionDefinition definition, Object transaction, boolean newTransaction,
    		boolean newSynchronization, boolean debug, Object suspendedResources) {
    
    	boolean actualNewSynchronization = newSynchronization &&
    			!TransactionSynchronizationManager.isSynchronizationActive();
    	return new DefaultTransactionStatus(
    			transaction, newTransaction, actualNewSynchronization,
    			definition.isReadOnly(), debug, suspendedResources);
    }

    当获取到一个新的事务后,需要设置事务的一些信息,比如隔离级别,timeout 等,这些功能不是由 Spring 来控制,而是由底层的数据库来控制的,数据库连接的设置是在 doBegin 方法中进行处理:

    protected void doBegin(Object transaction, TransactionDefinition definition) {
    	DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
    	// 数据库连接
    	Connection con = null;
    	//如果当前事务不存在数据库连接,或者,当前连接的事务同步设置为 true,则需要获取新的数据库连接
    	if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
    		// 获取新连接
    		Connection newCon = obtainDataSource().getConnection();
    		// 事务绑定新连接
    		txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
    	}
    	txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
    	con = txObject.getConnectionHolder().getConnection();
    	// 获取和设置隔离级别
    	Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
    	txObject.setPreviousIsolationLevel(previousIsolationLevel);
    
    	// 由 Spring 来控制提交方式
    	if (con.getAutoCommit()) {
    		txObject.setMustRestoreAutoCommit(true);
    		con.setAutoCommit(false);
    	}
    	prepareTransactionalConnection(con, definition);
    	// 设置当前线程存在事务的标志
    	txObject.getConnectionHolder().setTransactionActive(true);
    
    	// 获取和设置超时时间
    	int timeout = determineTimeout(definition);
    	if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
    		txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
    	}
    
    	//如果是新连接,则绑定到当前线程
    	if (txObject.isNewConnectionHolder()) {
    		TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
    	}
    	//其他代码......
    }
    
    // ====获取隔离级别
    public static Integer prepareConnectionForTransaction(Connection con, TransactionDefinition definition){
    	// 设置只读标识
    	if (definition != null && definition.isReadOnly()) {
    		con.setReadOnly(true);
    		//....
    	}
    
    	// 获取隔离级别
    	Integer previousIsolationLevel = null;
    	if (definition != null && definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
    		// 从数据库连接获取隔离级别
    		int currentIsolation = con.getTransactionIsolation();
    		if (currentIsolation != definition.getIsolationLevel()) {
    			previousIsolationLevel = currentIsolation;
    			con.setTransactionIsolation(definition.getIsolationLevel());
    		}
    	}
    	return previousIsolationLevel;
    }

    当设置完事务的信息后,需要把事务信息记录在当前线程中:

    protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
    	if (status.isNewSynchronization()) {
    		TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
    		TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
    			definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
    			definition.getIsolationLevel() : null);
    		TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
    		TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
    		TransactionSynchronizationManager.initSynchronization();
    	}
    }

    现在来处理已经存在事务的情况,

    if (isExistingTransaction(transaction)) {
    	return handleExistingTransaction(definition, transaction, debugEnabled);
    }

    判断是否存在事务,依据是事务中有连接,且 TransactionActive 为 true

    protected boolean isExistingTransaction(Object transaction) {
    	DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
    	return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
    }

    如果已经存在事务,则会根据事务的传播方式来进行处理,比如 requires_new, nested 等是如何处理:

    private TransactionStatus handleExistingTransaction(TransactionDefinition definition, Object transaction){
    	// 如果传播方式为 never, 则抛异常
    	if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
    		throw new IllegalTransactionStateException("...");
    	}
    	// 如果传播方式为 not_supported, 则把当前存在的事务挂起
    	if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
    		// 挂起当前事务
    		Object suspendedResources = suspend(transaction);
    		boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
    		return prepareTransactionStatus(definition, null, false, newSynchronization, debugEnabled, suspendedResources);
    	}
    	// 如果传播方式为 requires_new, 则挂起当前事务,新建一个新事务
    	if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
    		// 挂起当前事务
    		SuspendedResourcesHolder suspendedResources = suspend(transaction);
    		// 如果还没有激活事务,则新建事务
    		boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
    		DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, 
    											newSynchronization, debugEnabled, suspendedResources);
    		// 设置数据库的隔离级别,timeout等
    		doBegin(transaction, definition);
    		prepareSynchronization(status, definition);
    		return status;
    		//....
    	}
    	// 如果传播方式为 nested,则新建事务,但是不会把存在的事务挂起,它是一个子事务
    	if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
    		// 如果不支持嵌套事务,抛异常
    		if (!isNestedTransactionAllowed()) {
    			throw new NestedTransactionNotSupportedException("");
    		}
    		// 如果支持保存点,则创建保存点
    		if (useSavepointForNestedTransaction()) {
    			DefaultTransactionStatus status = prepareTransactionStatus(definition, transaction, 
    											 false, false, debugEnabled, null);
    			// 创建保存点								 
    			status.createAndHoldSavepoint();
    			return status;
    		}
    		else {
    			// 如果不支持保存点,则和 requires_new 是一样的
    			boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
    			DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, 
    												newSynchronization, debugEnabled, null);
    			doBegin(transaction, definition);
    			prepareSynchronization(status, definition);
    			return status;
    		}
    	}
    	// 如果传播方式为 supports和required
    	boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
    	return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
    }

    挂起事务,就是把当前事务的状态记录下来,后续在对该事务进行恢复。

    protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
    	if (TransactionSynchronizationManager.isSynchronizationActive()) {
    		List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
    		Object suspendedResources = null;
    		if (transaction != null) {
    			suspendedResources = doSuspend(transaction);
    		}
    		String name = TransactionSynchronizationManager.getCurrentTransactionName();
    		TransactionSynchronizationManager.setCurrentTransactionName(null);
    		boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
    		TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
    		Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
    		TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
    		boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
    		TransactionSynchronizationManager.setActualTransactionActive(false);
    		return new SuspendedResourcesHolder(suspendedResources, suspendedSynchronizations, 
    					name, readOnly, isolationLevel, wasActive);
    	}
    	//.....
    }
    // 挂起事务doSuspend
    protected Object doSuspend(Object transaction) {
    	DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
    	// 把事务的连接置空
    	txObject.setConnectionHolder(null);
    	// 从当前线程中移除
    	return TransactionSynchronizationManager.unbindResource(obtainDataSource());
    }

    当经过上面一系列操作获取到事务信息后,再根据事务信息来封装到 TransactionInfo 中:

    protected TransactionInfo prepareTransactionInfo(PlatformTransactionManager tm,
    		TransactionAttribute txAttr, String joinpointIdentification,
    		TransactionStatus status) {
    	// 封装事务信息
    	TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
    	if (txAttr != null) {
    		// 设置事务状态
    		txInfo.newTransactionStatus(status);
    
    	}
    }

    事务回滚

    到这里,目标方法执行之前的事务准备工作都已做好了,之后,会调用 InvocationCallback.proceedWithInvocation 来执行目标方法,如果执行失败,则会进行事务的回滚操作:

    protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
    	if (txInfo != null && txInfo.getTransactionStatus() != null) {
    		// 判断异常是不是 RunntimeException 和 Error
    		if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
    			// 回滚事务
    			txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
    			// .........
    		}
    		else {
    			// 如果是其他类型的异常,则正常提交
    			txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
    			// .......
    		}
    	}
    }
    
    //判断是否回滚的异常,当前可以通过rolbackFor属性来修改
    public boolean rollbackOn(Throwable ex) {
    	return (ex instanceof RuntimeException || ex instanceof Error);
    }

    回滚事务

     public final void rollback(TransactionStatus status) throws TransactionException {
     if (status.isCompleted()) {
     // 如果事务已完成,则回滚会抛异常
     throw new IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction");
            } else {
     DefaultTransactionStatus defStatus = (DefaultTransactionStatus)status;
     this.processRollback(defStatus);
            }
        }
    ​
     private void processRollback(DefaultTransactionStatus status) {
     try {
     try {
     this.triggerBeforeCompletion(status);
     // 如果有保存点,则回滚到保存点
     if (status.hasSavepoint()) {
     if (status.isDebug()) {
     this.logger.debug("Rolling back transaction to savepoint");
                        }
    ​
     status.rollbackToHeldSavepoint();
                    } else if (status.isNewTransaction()) {
     // 如果当前事务为独立的事务,则回滚
     if (status.isDebug()) {
     this.logger.debug("Initiating transaction rollback");
                        }
    ​
     this.doRollback(status);
                    } else if (status.hasTransaction()) {
     // 如果一个事务中又有事务,如 required,该事务可以看作一个事务链,
     //那么当其中的一个事务需要回滚的时候,并不是立马进行回滚,
     //而是只是设置回滚状态,到最后再统一回滚
     if (!status.isLocalRollbackOnly() && !this.isGlobalRollbackOnParticipationFailure()) {
     if (status.isDebug()) {
     this.logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
                            }
                        } else {
     if (status.isDebug()) {
     this.logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
                            }
          // 只是设置回滚状态
     this.doSetRollbackOnly(status);
                        }
                    } else {
     this.logger.debug("Should roll back transaction but cannot - no transaction available");
                    }
                } catch (RuntimeException var7) {
     this.triggerAfterCompletion(status, 2);
     throw var7;
                } catch (Error var8) {
     this.triggerAfterCompletion(status, 2);
     throw var8;
                }
       
     this.triggerAfterCompletion(status, 1);
            } finally {
     // 清空记录并恢复被挂起的事务
     this.cleanupAfterCompletion(status);
            }
    ​
        }
    ​
    
    

    事务的回滚操作,如果是嵌套事务,且有保存点的话,直接回滚到保存点,嵌套事务的回滚不会影响到外部事务,也就是说,外部事务不会回滚。回滚到保存点是根据底层数据库来操作的:

    public void rollbackToHeldSavepoint() throws TransactionException {
    	Object savepoint = getSavepoint();
    	// 回滚到保存点
    	getSavepointManager().rollbackToSavepoint(savepoint);
    	// 释放保存点
    	getSavepointManager().releaseSavepoint(savepoint);
    	setSavepoint(null);
    }
    // 回滚到保存点
    public void rollbackToSavepoint(Object savepoint) throws TransactionException {
    	ConnectionHolder conHolder = getConnectionHolderForSavepoint();
    	conHolder.getConnection().rollback((Savepoint) savepoint);
    	conHolder.resetRollbackOnly();
    	// ......
    
    }
    // 释放保存点
    public void releaseSavepoint(Object savepoint) throws TransactionException {
    	ConnectionHolder conHolder = getConnectionHolderForSavepoint();
    	conHolder.getConnection().releaseSavepoint((Savepoint) savepoint);
    }

    如果没有保存点,则直接回滚,也是使用数据库的API 来操作的:

    protected void doRollback(DefaultTransactionStatus status) {
    	DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
    	Connection con = txObject.getConnectionHolder().getConnection();
    	con.rollback();
    }

    还有一种情况, 如果一个事务中又有事务,如 required, 该事务可以看作一个事务链,那么当其中的一个事务需要回滚的时候,并不是立马进行回滚,而是只是设置回滚状态,到最后再统一回滚。

    事务回滚后需要对事务信息进行清除:

    private void cleanupAfterCompletion(DefaultTransactionStatus status) {
    	// 设置完成状态
    	status.setCompleted();
    	if (status.isNewSynchronization()) {
    		TransactionSynchronizationManager.clear();
    	}
    	if (status.isNewTransaction()) {
            // 清除事务信息
    		doCleanupAfterCompletion(status.getTransaction());
    	}
    	if (status.getSuspendedResources() != null) {
    		// 恢复被挂起的事务
    		Object transaction = (status.hasTransaction() ? status.getTransaction() : null);
    		resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());
    	}
    }

    清除事务信息:

    protected void doCleanupAfterCompletion(Object transaction) {
    	DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
    	// 从当前线程中移除数据库连接
    	if (txObject.isNewConnectionHolder()) {
    		TransactionSynchronizationManager.unbindResource(obtainDataSource());
    	}
    	//重置数据库连接
    	Connection con = txObject.getConnectionHolder().getConnection();
    	if (txObject.isMustRestoreAutoCommit()) {
    		con.setAutoCommit(true);
    	}
    	DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
    	// 如果是新连接,则释放连接
    	if (txObject.isNewConnectionHolder()) {
    		DataSourceUtils.releaseConnection(con, this.dataSource);
    	}
    	txObject.getConnectionHolder().clear();
    }

    恢复被挂起的事务:

    protected final void resume(Object transaction, SuspendedResourcesHolder resourcesHolder){
    	if (resourcesHolder != null) {
    		Object suspendedResources = resourcesHolder.suspendedResources;
    		if (suspendedResources != null) {
    			doResume(transaction, suspendedResources);
    		}
    		List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
    		if (suspendedSynchronizations != null) {
    			TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive);
    			TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);
    			TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);
    			TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);
    			doResumeSynchronization(suspendedSynchronizations);
    		}
    	}
    }
    // 恢复事务,把事务和当前线程绑定
    protected void doResume(Object transaction, Object suspendedResources) {
    	TransactionSynchronizationManager.bindResource(obtainDataSource(), suspendedResources);
    }

    事务提交

    当目标方法执行成功,没有抛出异常,则事务可以正常提交了;但是再上面分析事务回滚的时候,还有一种情况没有分析,就是如果一个事务嵌套再一个事务里面,是一个事务链,如果其中的某个事务需要回滚,它并不会真正的立马进行回滚,而是设置一个回滚标识,由最外层的事务来统一进行回滚;所以再提交事务之前,还需要进行判断。

     public final void commit(TransactionStatus status) throws TransactionException {
     // 如果事务已完成,则不能提交
     if (status.isCompleted()) {
     throw new IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction");
            } else {
     // 判断嵌套事务是否设置了回滚标识,如果嵌套事务设置了回滚标识,则整个事务链都不会提交
     DefaultTransactionStatus defStatus = (DefaultTransactionStatus)status;
     if (defStatus.isLocalRollbackOnly()) {
     if (defStatus.isDebug()) {
     this.logger.debug("Transactional code has requested rollback");
                    }
    ​
     this.processRollback(defStatus);
                } else if (!this.shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
     if (defStatus.isDebug()) {
     this.logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
                    }
    ​
     this.processRollback(defStatus);
     //需要注意此处,如果为嵌套事务,内部事务设置 setRollbackOnly 后,最外层事务会报错
     if (status.isNewTransaction() || this.isFailEarlyOnGlobalRollbackOnly()) {
     throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only");
                    }
                } else {
     // 提交事务
     this.processCommit(defStatus);
                }
            }
        }
    
    

    提交事务:

    private void processCommit(DefaultTransactionStatus status) throws TransactionException {
     try {
     boolean beforeCompletionInvoked = false;
    ​
     try {
     this.prepareForCommit(status);
     this.triggerBeforeCommit(status);
     this.triggerBeforeCompletion(status);
     beforeCompletionInvoked = true;
     boolean globalRollbackOnly = false;
     if (status.isNewTransaction() || this.isFailEarlyOnGlobalRollbackOnly()) {
     globalRollbackOnly = status.isGlobalRollbackOnly();
                    }
    ​
     if (status.hasSavepoint()) {
     if (status.isDebug()) {
     this.logger.debug("Releasing transaction savepoint");
                        }
    ​
     status.releaseHeldSavepoint();
                    } else if (status.isNewTransaction()) {
     if (status.isDebug()) {
     this.logger.debug("Initiating transaction commit");
                        }
    ​
     this.doCommit(status);
                    }
    ​
     if (globalRollbackOnly) {
     throw new UnexpectedRollbackException("Transaction silently rolled back because it has been marked as rollback-only");
                    }
                } catch (UnexpectedRollbackException var19) {
     this.triggerAfterCompletion(status, 1);
     throw var19;
                } catch (TransactionException var20) {
     if (this.isRollbackOnCommitFailure()) {
     this.doRollbackOnCommitException(status, var20);
                    } else {
     this.triggerAfterCompletion(status, 2);
                    }
    ​
     throw var20;
                } catch (RuntimeException var21) {
     if (!beforeCompletionInvoked) {
     this.triggerBeforeCompletion(status);
                    }
    ​
     this.doRollbackOnCommitException(status, var21);
     throw var21;
                } catch (Error var22) {
     if (!beforeCompletionInvoked) {
     this.triggerBeforeCompletion(status);
                    }
        // 如果提交过程中出现异常,则还是会回滚
     this.doRollbackOnCommitException(status, var22);
     throw var22;
                }
    ​
     try {
     this.triggerAfterCommit(status);
                } finally {
     this.triggerAfterCompletion(status, 0);
                }
            } finally {
     this.cleanupAfterCompletion(status);
            }
    ​
        }
     // 数据库连接进行回滚
     protected void doCommit(DefaultTransactionStatus status) {
     DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)status.getTransaction();
     Connection con = txObject.getConnectionHolder().getConnection();
     if (status.isDebug()) {
     this.logger.debug("Committing JDBC transaction on Connection [" + con + "]");
            }
    ​
     try {
     con.commit();
            } catch (SQLException var5) {
     throw new TransactionSystemException("Could not commit JDBC transaction", var5);
            }
        }
    
    到这里,Spring 事务的获取,提交,回滚去分析完毕了。
    展开全文
  • 事务管理在系统开发中是不可缺少的一部分,Spring提供了很好事务管理机制,主要分为编程式事务和声明式事务两种。关于事务的基础知识,如什么是事务,数据库事务以及Spring事务的ACID、隔离级别、传播机制、行为等,...

    事务管理在系统开发中是不可缺少的一部分,Spring提供了很好事务管理机制,主要分为编程式事务和声明式事务两种。

    关于事务的基础知识,如什么是事务,数据库事务以及Spring事务的ACID、隔离级别、传播机制、行为等,就不在这篇文章中详细介绍了。默认大家都有一定的了解。

    本文,作者会先简单介绍下什么是声明式事务和编程式事务,再说一下为什么我不建议使用声明式事务。

    编程式事务

    基于底层的API,如PlatformTransactionManager、TransactionDefinition 和 TransactionTemplate 等核心接口,开发者完全可以通过编程的方式来进行事务管理。

    编程式事务方式需要是开发者在代码中手动的管理事务的开启、提交、回滚等操作。

    public void test() {      TransactionDefinition def = new DefaultTransactionDefinition();      TransactionStatus status = transactionManager.getTransaction(def);       try {         // 事务操作         // 事务提交         transactionManager.commit(status);      } catch (DataAccessException e) {         // 事务提交         transactionManager.rollback(status);         throw e;      }}

    如以上代码,开发者可以通过API自己控制事务。

    声明式事务

    声明式事务管理方法允许开发者配置的帮助下来管理事务,而不需要依赖底层API进行硬编码。开发者可以只使用注解或基于配置的 XML 来管理事务。

    @Transactionalpublic void test() {     // 事务操作  }

    如上,使用@Transactional即可给test方法增加事务控制。

    当然,上面的代码只是简化后的,想要使用事务还需要一些配置内容。这里就不详细阐述了。

    这两种事务,格子有各自的优缺点,那么,各自有哪些适合的场景呢?为什么有人会拒绝使用声明式事务呢?

    声明式事务的优点

    通过上面的例子,其实我们可以很容易的看出来,声明式事务帮助我们节省了很多代码,他会自动帮我们进行事务的开启、提交以及回滚等操作,把程序员从事务管理中解放出来。

    声明式事务管理使用了 AOP 实现的,本质就是在目标方法执行前后进行拦截。 在目标方法执行前加入或创建一个事务,在执行方法执行后,根据实际情况选择提交或是回滚事务。

    使用这种方式,对代码没有侵入性,方法内只需要写业务逻辑就可以了。

    但是,声明式事务真的有这么好么?倒也不见得。

    声明式事务的粒度问题

    首先,声明式事务有一个局限,那就是他的最小粒度要作用在方法上。

    也就是说,如果想要给一部分代码块增加事务的话,那就需要把这个部分代码块单独独立出来作为一个方法。

    但是,正是因为这个粒度问题,本人并不建议过度的使用声明式事务。

    首先,因为声明式事务是通过注解的,有些时候还可以通过配置实现,这就会导致一个问题,那就是这个事务有可能被开发者忽略。

    事务被忽略了有什么问题呢?

    首先,如果开发者没有注意到一个方法是被事务嵌套的,那么就可能会再方法中加入一些如RPC远程调用、消息发送、缓存更新、文件写入等操作。

    我们知道,这些操作如果被包在事务中,有两个问题:

    1、这些操作自身是无法回滚的,这就会导致数据的不一致。可能RPC调用成功了,但是本地事务回滚了,可是PRC调用无法回滚了。

    2、在事务中有远程调用,就会拉长整个事务。那么久会导致本事务的数据库连接一直被占用,那么如果类似操作过多,就会导致数据库连接池耗尽。

    有些时候,即使没有在事务中进行远程操作,但是有些人还是可能会不经意的进行一些内存操作,如运算。或者如果遇到分库分表的情况,有可能不经意间进行跨库操作。

    但是如果是编程式事务的话,业务代码中就会清清楚楚看到什么地方开启事务,什么地方提交,什么时候回滚。这样有人改这段代码的时候,就会强制他考虑要加的代码是否应该方法事务内。

    有些人可能会说,已经有了声明式事务,但是写代码的人没注意,这能怪谁。

    话虽然是这么说,但是我们还是希望可以通过一些机制或者规范,降低这些问题发生的概率。

    比如建议大家使用编程式事务,而不是声明式事务。因为,作者工作这么多年来,发生过不止一次开发者没注意到声明式事务而导致的故障。

    因为有些时候,声明式事务确实不够明显。

    声明式事务用不对容易失效

    除了事务的粒度问题,还有一个问题那就是声明式事务虽然看上去帮我们简化了很多代码,但是一旦没用对,也很容易导致事务失效。

    如以下几种场景就可能导致声明式事务失效:

    1、@Transactional 应用在非 public 修饰的方法上

    2、@Transactional 注解属性 propagation 设置错误

    3、@Transactional 注解属性 rollbackFor 设置错误

    4、同一个类中方法调用,导致@Transactional失效

    5、异常被catch捕获导致@Transactional失效

    6、数据库引擎不支持事务

    以上几个问题,如果使用编程式事务的话,很多都是可以避免的。

    使用声明事务失效的问题我们发生过很多次。不知道大家有没有遇到过,我是实际遇到过的

    因为Spring的事务是基于AOP实现的,但是在代码中,有时候我们会有很多切面,不同的切面可能会来处理不同的事情,多个切面之间可能会有相互影响。

    在之前的一个项目中,我就发现我们的Service层的事务全都失效了,一个SQL执行失败后并没有回滚,排查下来才发现,是因为一位同事新增了一个切面,这个切面里面做个异常的统一捕获,导致事务的切面没有捕获到异常,导致事务无法回滚。

    这样的问题,发生过不止一次,而且不容易被发现。

    很多人还是会说,说到底还是自己能力不行,对事务理解不透彻,用错了能怪谁。

    但是我还是那句话,我们确实无法保证所有人的能力都很高,也无法要求所有开发者都能不出错。我们能做的就是,尽量可以通过机制或者规范,来避免或者降低这些问题发生的概率。

    其实,如果大家有认真看过阿里巴巴出的那份Java开发手册的话,其实就能发现,其中的很多规约并不是完完全全容易被人理解,有些也比较生硬,但是其实,这些规范都是从无数个坑里爬出来的开发者们总结出来的。

    关于@Transactional的用法,规约中也有提到过,只不过规约中的观点没有我这么鲜明:

    abca2564604d5c2515a44ab1b8e2a18d.png

    总结

    最后,相信本文的观点很多人都并不一定认同,很多人会说:Spring官方都推荐无侵入性的声明式事务,你有啥资格出来BB 。

    说实话,刚工作的前几年,我也热衷于使用声明式事务,觉得很干净,也很"优雅"。觉得师兄们使用编程式事务多此一举,没有工匠精神。

    但是慢慢的,线上发生过几次问题之后,我们复盘后发现,很多时候你自己写的代码很优雅,这完全没问题。

    但是,优雅的同时也带来了一些副作用,师兄们又不能批评我,因为我的用法确实没错…

    所以,有些事,还是要痛过之后才知道。

    当然,本文并不要求大家一定要彻底不使用声明式事务,只是建议大家日后在使用事务的时候,能够考虑到本文中提到的观点,然后自行选择。

    关于作者:Hollis(ID:hollischuang),一个对Coding有着独特追求的人,现任阿里巴巴技术专家,个人技术博主,技术文章全网阅读量数千万,《程序员的三门课》联合作者。

    展开全文
  • 1.项目背景发生服务雪崩的项目是一个支付的核心服务,交易提现服务2.项目发生现象线程在执行过程中,会执行到某个方法的时候,就停止执行,日志也不打印...发生线程暂停的起止点的事务都是@Transactional(rollbackFor...

    e9ec3f6318723b451f9a2c36677b3848.png

    1.项目背景

    发生服务雪崩的项目是一个支付的核心服务,交易提现服务

    2.项目发生现象

    线程在执行过程中,会执行到某个方法的时候,就停止执行,日志也不打印。发生这个的时候,整个服务的所有执行操作都会停下来,导致整个服务不可用。

    3.猜测

    通过日志观察,所有的线程都是在要执行事务的时候会停下来不执行了。这个时候观察事物的使用方式。发生线程暂停的起止点的事务都是@Transactional(rollbackFor=Exception.class )嵌套 REQUIRES_NEW 事务,这两个事务嵌套的执行原理是父事务执行了,然后挂起事务去执行REQUIRES_NEW 子事务,子事务提交后,释放父事务然后提交父事务。等待是死锁的一个很关键的点。所以服务的雪崩一定和这个地方的事务使用有关

    4.验证猜测

    本地手动起并发执行@Transactional(rollbackFor=Exception.class )嵌套 REQUIRES_NEW 事务,当并发数达到20以上的时候,死锁的概率发生率近100%,,接着就是下一个问题了。为什么会发生事务死锁。事务的创建过程中是要提前去数据库申请数据库资源,这个时候问题又衍生到数据库的线程池配置了。
    查看线程池配置,配置连接池配的1000,链接超时时间60秒,可是事务链接死锁60秒并未断开,查看检验发现链接池配置并未生效................

    5.产生原因

    连接池配置未生效,默认为8。导致并发嵌套事物的时候,外部创建完时候,执行子嵌套事务的事务,连接池数量不够等待连接池,外部又在等待内部事务提交,内部事务等待链接池。发生连接池一直占用死锁。导致整个服务的事务都创建等待服务雪崩

    6.解决方法

    手动装配连接池配置后并发死锁情况解决,死锁链接超时后自动断开链接释放连接池资源

    也算是前同事留下来的一颗坑..................

    展开全文
  • 事务管理在系统开发中是不可缺少的一部分,Spring提供了很好事务管理机制,主要分为编程式事务和声明式事务两种。关于事务的基础知识,如什么是事务,数据库事务以及Spring事务的ACID、隔离级别、传播机制、行为等,...
  • 事务回滚@Transactional手动提交回滚

    千次阅读 2020-07-02 17:24:03
    (注意:若是在方法类中使用try、catch捕获异常则不会回滚该事务) 2、手动提交回滚 impl中通过这样获取: @Resource(name="transactionManager") private DataSourceTransactionManager transactionManager;...
  • spring 方法加了@Transactional,我想循环一次就提交,怎么搞,开启了事务默认应该是执行完了才全部提交
  • Spring手动提交事务

    2021-01-07 14:04:29
    Spring事务手动提交 事务背景: Spring事务开启,常见的可以用基于注解的方式进行: @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) 手动进行Sring事务的提交 ...
  • 今天在工作中遇到了一个spring事务的问题: 在service方法内需要通过线程去执行添加用户积分和用户积分日志的情况,试了下通过@Transactional声明式事务不起作用,只能使用手动事务去控制 因此写了这篇博客,记录一下这...
  • 在 SpringBoot 中使用事务很简单,只需要在 CRUD 方法上加上 @Transactional 注解即可@EnableTransactionManagement 启注解事务管理,等同于 xml 配置方式的 @Transactional @Service public class UserServiceImpl ...
  • 手动分步提交事务

    2020-07-23 20:41:13
    同时插入两张表,一张表没有插入数据,则执行事务回滚 @Transactional(timeout = 5,rollbackFor = Exception.class) public Boolean addDeviceFaceInfo(TFaceInfoRecordWithBLOBs faceInfoRecord, ...
  • springboot事务手动提交

    2021-02-02 15:16:58
    @Autowired private PlatformTransactionManager platformTransactionManager; @Transactional public void insert() { try { // 业务逻辑 ... // 事务手动回滚 platformTransactionManager.rollb
  • 手动事务提交

    2019-12-02 17:09:54
    我们现在进行事务控制一般都是使用注解型事务,然而有些时候却会发现注解型事务失效了。比如下方的这种情况: @Transactional(propagation= Propagation.REQUIRES_NEW,rollbackFor = Exception.class) @Override ...
  • 我使用Spring / Spring-data-JPA,发现自己需要在单元测试中手动强制提交。我的用例是,我正在进行一个多线程测试,其中我必须使用在线程生成之前持久化的数据。不幸的是,考虑到测试在@Transactional事务中运行,...
  • 处理springboot 下提交事务异常,数据库没有回滚的问题。spring的文档中说道,spring声明式事务管理默认对非检查型异常和运行时异常进行事务回滚,而对检查型异常则不进行回滚操作。什么是检查型异常什么又是非检查...
  • 自动提交事务 如何设置在测试方法执行后,将事务提交呢? 如下: @Test @Transactional @Rollback(false) public void testUpdateFirstWebClmCheckclmTPAVO() throws Exception { HashMap<String, Object>...
  • 所谓的声明式事务即为在配置文件中配置,无需程序员手动编程控制事务,也就是说数据库的事务的开启,提交都是框架帮助我们做好的,然而,编程式事务,是需要在方法中加入Spring的事务API 例如hibernate中的...
  • 配置好Spring事务之后(Spring事务配置,可参见其它文件),我们可以简单的添加一个注释@Transactional实现事务, 但是如果方法中Catch了异常,此异常没有抛出,那么事务将不会生效。 可以在Catch中加入如下代码...
  • 处理springboot 下提交事务异常,数据库没有回滚的问题。 spring的文档中说道,spring声明式事务管理默认对非检查型异常和运行时异常进行事务回滚,而对检查型异常则不进行回滚操作。 什么是检查型异常什么又是非...
  • Springboot事务手动回滚或提交

    千次阅读 2020-05-09 14:32:14
    Springboot事务手动回滚或提交 service层的方法被controller层调用时,在方法上加@Transactional注解,操作数据库时会有事务。 @Transactional public void insert() { try { // 业务逻辑 } catch (Exception ...
  • 编程式事务:就是直接在代码里手动开启事务手动提交,手动回滚。优点就是可以灵活控制,缺点就是太麻烦了,太多重复的代码了。 声明式事务:就是使用SpringAop配置事务,这种方式大大的简化了编码。需要注意的是...
  • 1.Spring事务的原理 Spring 事务管理分为编码式和声明式的两种方式。...使用@Transactional的相比传统的我们需要手动开启事务,然后提交事务来说。它提供如下方便 根据你的配置,设置是否自动开启事务 自动提交
  • 编程式事务也就是用代码手动控制事务的开始、提交或回滚,这样业务代码就变得不纯粹,功能代码和辅助代码杂糅到一起,并且会有许多重复代码。所以我们一般使用声明式事务。声明式事务一般有两种方式,一是基于tx和...
  • Spring框架提供了便捷的事务管理。不会对业务调用造成任何干扰。不需要手动开启,提交事务,只需要在类或者方法上进行少量的注解就可以自动完成这些操作。本篇主要介绍了@Transactional注解的使用
  • 事务Transactional注解的失效场景 ...编程式事务:是指在代码中手动的管理事务提交、回滚等操作,代码侵入性比较强,如下示例: try { //TODO something transactionManager.commit(status)...
  • 亲测在使用@Transactional、@Transactional(rollbackFor = Exception.class)及catch...下面使用线程所机制,进行整体的事务提交事务回滚,代码如下:在springboot启动类上加 @EnableTransactionManagement 注解...
  • 在catch代码块里加上TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 表示手动提交事务
  • 手动实现 spring 事务

    2020-07-04 17:38:03
    手动实现 spring 事务替换 @Transactional ## 自定义注解 @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface ExtTransaction { } TransactionUtils // 编程事务(需要手动...
  • 亲测在使用@Transactional、@Transactional(rollbackFor = Exception.class)及catch...下面使用线程所机制,进行整体的事务提交事务回滚,代码如下:在springboot启动类上加 @EnableTransactionManagement 注解...

空空如也

空空如也

1 2 3 4 5
收藏数 95
精华内容 38
关键字:

transactional手动提交事务