栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

spring中的事务

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

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

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

哪些情况事务不起作用

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

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();
      }
   }
}
 
大致流程 

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号