spring项目中使用@Transactional注解,事务不生效的坑

spring中的事务

分为两种:一种是编程式事务,一种是声明式事务

顾名思义,编程式事务是指通过代码去实现事务管理,这里不做过多说明。

另一种是声明式事务,分为两种情况01:一种是通过传统xml方式配置,02:使用@Transactional注解方式配置,这是主要讲解的是通过注解方式配置。因为在springboot项目中,会自动配置DataSourceTransactionManager,我们只需要在对应的方法上或者类上加上@Transactional就会自动接入到spring的事务中,让spring管理,只有代理类的事务才会被spring管理起来。默认情况下只有运行时异常和error的时候事务才会回滚。

哪些情况事务不起作用

如下图所示,我这边本地调用接口修改数据库张三口袋里面的金额,并且启用了事务管理,抛出RuntimeExecption。这时我们调用接口,我们可以看到事务生效了,数据库里面值并没有发生改变。但是,当我们把抛出的异常改为
1、throw new SQLTimeoutException(); 调用接口的时候,发现数据库张三的金额被改变了,事务没起作用,明明开启了事务,但是没起作用,这是为什么呢?

spring项目中使用@Transactional注解,事务不生效的坑_第1张图片

2、在我们需要执行事务的方法,如果对异常进行抛出,并且我们手动捕获了这个异常的话,这时候事务也不会起作用的。如下图所示: 

spring项目中使用@Transactional注解,事务不生效的坑_第2张图片

3、@Transactional注解只对方法名为pubic的才生效,编译器中不是public的方法会提示错误,对其他事务不会生效。 

4、默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。有事务注解修饰的方法调用类中其他非事务的update操作,非事务的方法在发生异常或者错误时不会随着主方法一起回滚。

解决方案

1:Spring的事务管理默认是针对Error异常和RuntimeException异常以及其子类进行事务回滚。对RuntimeException并不需要抛出,Error不需要抛出异常、也不进行捕获。所以我们上面用到的SQLTimeoutException()并不属于这两者之间,我们需要手动回滚异常,在@Transactional注解里面指定回滚异常类型即可,我这里举一个例子@Transactional(rollbackFor = Exception.class)

2: 我们在需要执行的sercvice里面不应该主动捕获异常,这会导致我们事务不生效,应该继续往上抛,在controller层捕获即可,这样事务也生效了,异常也捕获了。

3:@Transaction注解只对方法名为pubic的才生效,其他事务不会生效。顾名思义,也就是说使用了@Transaction注解的,只能是public。因为只有@Transaction注解只有被其他方法调用才生效的,能被其他方法调用的方法,只能是public。

4:我们在使用事务注解的时候,尽量不要在类上面使用,这会使得类里面的所有方法都会有事务进行处理。比如说,我们一些方法只做查询操作,我们就没有必要再进行事务,我们应该在需要事务处理的方法上面加事务,并且指定回滚的异常类型。

事务开始,提交或者回滚,都会触发相应的事务事件

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@EventListener
public @interface TransactionalEventListener {
  // 指定当前标注方法处理事务的类型
	TransactionPhase phase() default TransactionPhase.AFTER_COMMIT;
  // 用于指定当前方法如果没有事务,是否执行相应的事务事件监听器
	boolean fallbackExecution() default false;
  // 与classes属性一样,指定了当前事件传入的参数类型,指定了这个参数之后就可以在监听方法上
  // 直接什么一个这个参数了
	@AliasFor(annotation = EventListener.class, attribute = "classes")
	Class[] value() default {};
  // 作用于value属性一样,用于指定当前监听方法的参数类型
	@AliasFor(annotation = EventListener.class, attribute = "classes")
	Class[] classes() default {};
  // 这个属性使用Spring Expression Language对目标类和方法进行匹配,对于不匹配的方法将会过滤掉
	String condition() default "";
}

 事务拦截器TransactionInterceptor

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.transaction.interceptor;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Properties;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.lang.Nullable;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.interceptor.TransactionAspectSupport.InvocationCallback;

public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
    public TransactionInterceptor() {
    }

    public TransactionInterceptor(PlatformTransactionManager ptm, Properties attributes) {
        this.setTransactionManager(ptm);
        this.setTransactionAttributes(attributes);
    }

    public TransactionInterceptor(PlatformTransactionManager ptm, TransactionAttributeSource tas) {
        this.setTransactionManager(ptm);
        this.setTransactionAttributeSource(tas);
    }

    @Nullable
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Class targetClass = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null;
        Method var10001 = invocation.getMethod();
        invocation.getClass();
        return this.invokeWithinTransaction(var10001, targetClass, invocation::proceed);
    }

    private void writeObject(ObjectOutputStream oos) throws IOException {
        oos.defaultWriteObject();
        oos.writeObject(this.getTransactionManagerBeanName());
        oos.writeObject(this.getTransactionManager());
        oos.writeObject(this.getTransactionAttributeSource());
        oos.writeObject(this.getBeanFactory());
    }

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        this.setTransactionManagerBeanName((String)ois.readObject());
        this.setTransactionManager((PlatformTransactionManager)ois.readObject());
        this.setTransactionAttributeSource((TransactionAttributeSource)ois.readObject());
        this.setBeanFactory((BeanFactory)ois.readObject());
    }
}

protected Object invokeWithinTransaction(Method method, Class targetClass, final InvocationCallback invocation)
      throws Throwable {
   // If the transaction attribute is null, the method is non-transactional.
   final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
   final PlatformTransactionManager tm = determineTransactionManager(txAttr);
   final String joinpointIdentification = methodIdentification(method, targetClass);
   if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
      // 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;
   }
   else {
      // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
      try {
         Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
               new TransactionCallback() {
                  @Override
                  public Object doInTransaction(TransactionStatus status) {
                     TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
                     try {
                        return invocation.proceedWithInvocation();
                     }
                     catch (Throwable ex) {
                        if (txAttr.rollbackOn(ex)) {
                           // A RuntimeException: will lead to a rollback.
                           if (ex instanceof RuntimeException) {
                              throw (RuntimeException) ex;
                           }
                           else {
                              throw new ThrowableHolderException(ex);
                           }
                        }
                        else {
                           // A normal return value: will lead to a commit.
                           return new ThrowableHolder(ex);
                        }
                     }
                     finally {
                        cleanupTransactionInfo(txInfo);
                     }
                  }
               });
         // Check result: It might indicate a Throwable to rethrow.
         if (result instanceof ThrowableHolder) {
            throw ((ThrowableHolder) result).getThrowable();
         }
         else {
            return result;
         }
      }
      catch (ThrowableHolderException ex) {
         throw ex.getCause();
      }
   }
}
 
  

大致流程

spring项目中使用@Transactional注解,事务不生效的坑_第3张图片

你可能感兴趣的:(spring,boot,spring,事务不生效,1024程序员节)