Spring事务失效,我总结了这7个主要原因

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 本文详细探讨了Spring事务在日常开发中常见的七个失效原因,包括数据库不支持事务、类不受Spring管理、事务方法非public、异常被捕获、`rollbackFor`属性配置错误、方法内部调用事务方法及事务传播属性使用不当。通过具体示例和源码分析,帮助开发者更好地理解和应用Spring事务机制,避免线上事故。适合所有使用Spring进行业务开发的工程师参考。

你好,我是猿java。

提起事务,很多人自然而然想到 MySQL数据库的 ACID,而 Spring Transaction 作为业务层的事务,也是Java日常开发中必不可少的。然而在工作中,很多开发认为只要在类或者方法上加上 @Transactional注解就可以万事大吉了,
殊不知事务失效的场景有多种,一个疏忽就可能造成了线上事故,这篇文章我来和你聊一聊导致Spring 事务失效的 7个主要原因,帮助你的日常工作避雷。

申明:本文是基于 Spring 5.3.21 版本,如果没有特殊说明,本文的数据库泛指 MySQL数据库

内容大纲:
img.png

1.数据库不支持事务

Spring 事务是业务层的事务,其底层还是依赖于数据库本身的事务支持。比如 MySQL 数据库,MyISAM 引擎不支持事务而 InnoDB 引擎支持事务。所以,开发中如果需要使用事务,一定要确保你选择的数据库支持事务。

MySQL数据库查看表信息指令如下,信息中包含表的使用的引擎类型:

show create table tablename\G

sql-transaction.png

从 MySQL 5.5.5版本开始,默认的数据库引擎已经从 MyISAM 更改成 InnoDB。所以,如果你的数据库低于这个版本,就得注意数据库或者表在创建的时候设置的引擎是 MyISAM 还是 InnoDB。

2.类不受 Spring管理

我们先看一段 Spring事务失效的代码:

// @Service
public class UserServiceImpl implements UserService {
   

    private final UserRepository userRepository;

    public UserServiceImpl(UserRepository userRepository) {
   
        this.userRepository = userRepository;
    }
    /* user{ "name": "name1", "age": 18 } */
    @Transactional
    @Override
    public void addUser(User user) {
   
        userRepository.save(user);
        // 模拟异常,数据库的记录应该回滚
        throw new RuntimeException();
    }
}

如上代码,将 UserServiceImpl类中的 @Service注解注释后,UserServiceImpl类就不受 Spring容器管理,导致事务失效!

事务为什么会失效?

这是因为 Spring事务是由 AOP机制实现的,AOP机制的本质就是动态代理,更直白的说:从 Spring IOC容器获取 bean时,Spring会为目标类创建代理,从而支持事务的,核心源码如下:

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
  implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
   

    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
   
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
   
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
   
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
   
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // Create proxy if we have advice.
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
   
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            // 创建 AOP代理
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

  protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
                               @Nullable Object[] specificInterceptors, TargetSource targetSource) {
   

    if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
   
      AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
    }

    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);
    // 部分代码省略
    // 创建 AOP 切面,将信息存放在代理中
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    proxyFactory.addAdvisors(advisors);
    proxyFactory.setTargetSource(targetSource);
    customizeProxyFactory(proxyFactory);

    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
   
      proxyFactory.setPreFiltered(true);
    }
    // Use original ClassLoader if bean class not locally loaded in overriding class loader
    ClassLoader classLoader = getProxyClassLoader();
    if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
   
      classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
    }
    return proxyFactory.getProxy(classLoader);
  }
}

通过源码可以看出:事务需要的 Advisor等资源是在 Spring创建代理类时去创建的,因此,注释 UserServiceImpl类的 @Service注解,该类就不受 Spring容器管理,那么事务需要的 Advisor资源就无法生成,事务自然就失效了。

不过,需要说明的是,目前 Java业务开发几乎都采用了 Spring生态圈的框架,如果将 UserServiceImpl类的 @Service注解去掉:

假如 UserServiceImpl类是通过构造器注入到Controller层,则 IDEA会报错,编译通过不了,如下图:
spring-transaction-construct.png

假如 UserServiceImpl类是通过 @Autowired字段依赖注入,运行时会出现空指针异常,导致整个业务逻辑异常中断。

所以,"类不受 Spring管理"这个原因在现实开发中发生的概率比较低,但是该 case可以帮助我们更好的了解 Spring事务机制,所以被小编作为一个主要原因。

3.事务方法不是 public

假如对 UserServiceImpl类做部分修改,将事务方法 addUser()修改成 protected,代码如下:


@Service
public class UserServiceImpl /** implements UserService */{
   

    private final UserRepository userRepository;

    public UserServiceImpl(UserRepository userRepository) {
   
        this.userRepository = userRepository;
    }

    /* user{ "name": "name1", "age": 18 } */
    @Transactional
    //@Override
    protected void addUser(User user) {
   
        userRepository.save(user);
        // 模拟异常,数据库的记录应该回滚
        throw new RuntimeException();
    }
}

单测运行上面的代码,查询数据库中记录,如下图:

img.png

通过 client客户端查询user表数据发现:{ "name": "name1", "age": 18 } 记录存在数据库中,事务并未回滚,也就是事务失效了,不符合预期。

为什么事务会失效?

我们还是通过 Spring的官方核心源码来分析:

public abstract class AbstractFallbackTransactionAttributeSource
  implements TransactionAttributeSource, EmbeddedValueResolverAware {
   

  // 获取事务属性
  @Override
  @Nullable
  public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
   
      // 部分代码省略

      if (cached != null) {
   
        // 部分代码省略
      }
      else {
   
        // 调用 计算事务属性 方法
        // We need to work it out.
        TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
        // 部分代码省略
        return txAttr;
      }
  }

     // 计算事务属性
    @Nullable
    protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
   
        // 按照配置,不允许使用非公共方法。
        // Don't allow non-public methods, as configured.
        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
   
            return null;
        }

        // 部分代码省略
        return null;
    }
}

通过源码,我们可以很清晰的看出,getTransactionAttribute()获取事务属性方法会调用 computeTransactionAttribute()方法,而computeTransactionAttribute()方法的逻辑,判断当方法非 public,则直接返回null,因此事务属性都不存在,事务自然也就不生效了。

4.异常被业务代码 catch

我们再对 UserServiceImpl类做代码调整,将 addUser()方法里的业务逻辑加上 try-catch,代码如下:


@Service
public class UserServiceImpl implements UserService {
   

    private final UserRepository userRepository;

    public UserServiceImpl(UserRepository userRepository) {
   
        this.userRepository = userRepository;
    }

    /* user{ "name": "name2", "age": 20 } */
    @Transactional
    @Override
    public void addUser(User user) {
   
       try{
   
         userRepository.save(user);
         // 模拟异常,数据库的记录应该回滚
         throw new RuntimeException();
       } catch (RuntimeException e) {
   
         log.error("addUser#error,user:{},e", user, e);
       }
    }
}

单测运行上面的代码,查询数据库中记录,如下图:
spring-transaction-try.png

通过 client客户端查询user表数据发现:{ "name": "name2", "age": 20 } 记录存在数据库中,并未回滚,也就是事务失效了,不符合预期。

为什么事务没有回滚?

我们依旧来看看 Spring的官方核心源码:

public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean {
   
  @Nullable
  protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
                                           final InvocationCallback invocation) throws Throwable {
   
      // 省略部分代码

      if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
   
          Object retVal;
          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
              // 回滚事务 是在 spring 的catch中处理,也就是说,如果Spring catch不到对应的异常,就不会进入回滚事务的逻辑
              completeTransactionAfterThrowing(txInfo, ex);
              throw ex;
          } finally {
   
              cleanupTransactionInfo(txInfo);
          }
          // 省略部分代码

          // 提交事务
          commitTransactionAfterReturning(txInfo);
          return retVal;
      }
  }

  /**
   * Handle a throwable, completing the transaction.
   * We may commit or roll back, depending on the configuration.
   * @param txInfo information about the current transaction
   * @param ex throwable encountered
   */
  protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
   
    if (txInfo != null && txInfo.getTransactionStatus() != null) {
   
       // 省略部分代码
      if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
   
        try {
   
            // 回滚事务
          txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
        }
        catch (TransactionSystemException ex2) {
   
          logger.error("Application exception overridden by rollback exception", ex);
          ex2.initApplicationException(ex);
          throw ex2;
        }
        catch (RuntimeException | Error ex2) {
   
          logger.error("Application exception overridden by rollback exception", ex);
          throw ex2;
        }
      }
        // 省略部分代码
    }
  }
}

通过源码,我们可以很清晰的看出,在 invokeWithinTransaction() 方法中,当 Spring catch到 Throwable异常,就会调用 completeTransactionAfterThrowing()方法进行事务回滚的逻辑。但是,在 UserServiceImpl类的业务代码中直接把异常catch住了,Spring自然就 catch不到异常,因此事务回滚的逻辑就不会执行,事务就失效了。

所以,在日常开发中,当我们在 try-catch做完一些常规操作(比如异常日志记录和监控打点)后,一定要记得把异常抛出来(特别注意抛出的异常一定要是 Throwable或其子类,原因在下文会分析),这样 Spring才能感知异常,回滚事务。

5.rollbackFor属性配置错误

我们接着对 UserServiceImpl类做修改,在 @Transactional注解里指定 rollbackFor属性,比如:@Transactional(rollbackFor = Error.class),代码如下:

@Service
public class UserServiceImpl implements UserService {
   

    private final UserRepository userRepository;

    public UserServiceImpl(UserRepository userRepository) {
   
        this.userRepository = userRepository;
    }

    /* user{ "name": "name3", "age": 30 } */
    @Transactional(rollbackFor = Error.class)
    @Override
    public void addUser(User user) {
   
      userRepository.save(user);
      // 模拟异常,数据库的记录应该回滚
      throw new Exception();
    }
}

单测运行上面的代码后,查询数据库中记录,如下图:

spring-transaction-rollback.png

通过 client客户端查询user表数据发现:{ "name": "name3", "age": 30 } 记录存在数据库中,并未回滚,也就是事务失效了,不符合预期。

为什么事务没有回滚?

我们依旧来看看 Spring的官方核心源码:


@Target({
   ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
   
  /**
   * Defines zero (0) or more exception classes, which must be subclasses of Throwable, indicating which exception types must cause a transaction rollback.
   * By default, a transaction will be rolled back on RuntimeException and Error but not on checked exceptions (business exceptions). See org.springframework.transaction.interceptor.DefaultTransactionAttribute.rollbackOn(Throwable) for a detailed explanation.
   * This is the preferred way to construct a rollback rule (in contrast to rollbackForClassName), matching the exception type, its subclasses, and its nested classes. See the class-level javadocs for further details on rollback rule semantics and warnings regarding possible unintentional matches.
   * @return
   */
  //  rollbackFor的异常必须是 Throwable的子类
  Class<? extends Throwable>[] rollbackFor() default {
   };
}

通过 Transactional注解源码,我们可以发现 rollbackFor属性指定的异常必须是 Throwable及其子类,并且在默认情况下,Spring对 RuntimeException 和 Error 两种异常会自动回滚事务,也就是说,如果业务抛出来的异常是 RuntimeException 和 Error类型,可以不需要通过 rollbackFor属性指定,Spring 默认会识别处理。

可是,UserServiceImpl类中 rollbackFor = Error.class,Error 明明是 Throwable的子类,为什么事务还是会失效?

这是因为,尽管业务代码指定了 rollbackFor = Error.class,但是代码中抛出的异常是 Exception(throw new Exception()),而 Exception 和 Error没有任何关系,也就是说,事务需要捕获到 Error才会回滚,可是你偏偏抛出一个和 Error不相关的 Exception异常,因此事务自然无效,不能回滚。

6.方法内部调用事务方法

我们接着对 UserServiceImpl类做修改,addUser()方法不加 @Transactional注解,而是在 addUser()内部调用一个加了 @Transactional注解的 doAddUser()方法,代码如下:

@Service
public class UserServiceImpl implements UserService {
   

    private final UserRepository userRepository;

    public UserServiceImpl(UserRepository userRepository) {
   
        this.userRepository = userRepository;
    }

  /* user{ "name": "name4", "age": 40 } */
  @Override
  public void addUser(User user){
   
    // 调用内部方法
    this.doAddUser(user);
  }

  @Transactional(rollbackFor = Error.class)
  public void doAddUser (User user){
   
    userRepository.save(user);
    // 模拟异常,数据库的记录应该回滚
    throw new RuntimeException();
  }
}

单测运行上面的代码,查询数据库中记录,如下图:

spring-transaction-inter.png

通过 client客户端查询user表数据发现:{ "name": "name4", "age": 40 } 记录存在数据库中,并未回滚,事务失效,不符合预期。

为什么事务没有回滚?

从上文"事务方法不是 public" 的分析可以知道,事务是通过Spring AOP代理来实现的,而 doAddUser()内部事务方法其实是this对象调用的,而不是通过AOP代理来调用的,因此事务失效。

7. 事务传播属性使用错误

我们最后对 UserServiceImpl类做修改,addUser()方法不加 @Transactional注解,而是在 addUser()内部调用一个加了 @Transactional注解的 doAddUser()方法,代码如下:

@Service
public class UserServiceImpl implements UserService {
   

    private final UserRepository userRepository;

    public UserServiceImpl(UserRepository userRepository) {
   
        this.userRepository = userRepository;
    }

  /* user{ "name": "name5", "age": 50 } */
  @Transactional(propagation = Propagation.SUPPORTS)
  @Override
  public void addUser(User user){
   
    this.doAddUser(user);
  }


  @Transactional
  public void doAddUser (User user){
   
    userRepository.save(user);
    throw new RuntimeException();
  }
}

单测运行上面的代码,查询数据库中记录,如下图:

spring-transaction-propagation.png

通过 client客户端查询user表数据发现:{ "name": "name5", "age": 50 } 记录存在数据库中,并未回滚,事务失效,不符合预期。

为什么事务没有回滚?

我们还是从 Spring源码来找原因:

@Target({
   ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
   
  /**
   * The transaction propagation type.
   * <p>Defaults to {@link Propagation#REQUIRED}.
   * @see org.springframework.transaction.interceptor.TransactionAttribute#getPropagationBehavior()
   */
  Propagation propagation() default Propagation.REQUIRED;
}

public enum Propagation {
   

  /**
   * Support a current transaction, create a new one if none exists.
   * Analogous to EJB transaction attribute of the same name.
   * <p>This is the default setting of a transaction annotation.
   */
  REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),

  /**
   * Support a current transaction, execute non-transactionally if none exists.
   * Analogous to EJB transaction attribute of the same name.
   * <p>Note: For transaction managers with transaction synchronization,
   * {@code SUPPORTS} is slightly different from no transaction at all,
   * as it defines a transaction scope that synchronization will apply for.
   * As a consequence, the same resources (JDBC Connection, Hibernate Session, etc)
   * will be shared for the entire specified scope. Note that this depends on
   * the actual synchronization configuration of the transaction manager.
   * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setTransactionSynchronization
   */
  SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),

  /**
   * Support a current transaction, throw an exception if none exists.
   * Analogous to EJB transaction attribute of the same name.
   */
  MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),

  /**
   * Create a new transaction, and suspend the current transaction if one exists.
   * Analogous to the EJB transaction attribute of the same name.
   * <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-box
   * on all transaction managers. This in particular applies to
   * {@link org.springframework.transaction.jta.JtaTransactionManager},
   * which requires the {@code javax.transaction.TransactionManager} to be
   * made available to it (which is server-specific in standard Java EE).
   * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
   */
  REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),

  /**
   * Execute non-transactionally, suspend the current transaction if one exists.
   * Analogous to EJB transaction attribute of the same name.
   * <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-box
   * on all transaction managers. This in particular applies to
   * {@link org.springframework.transaction.jta.JtaTransactionManager},
   * which requires the {@code javax.transaction.TransactionManager} to be
   * made available to it (which is server-specific in standard Java EE).
   * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
   */
  NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),

  /**
   * Execute non-transactionally, throw an exception if a transaction exists.
   * Analogous to EJB transaction attribute of the same name.
   */
  NEVER(TransactionDefinition.PROPAGATION_NEVER),

  /**
   * Execute within a nested transaction if a current transaction exists,
   * behave like {@code REQUIRED} otherwise. There is no analogous feature in EJB.
   * <p>Note: Actual creation of a nested transaction will only work on specific
   * transaction managers. Out of the box, this only applies to the JDBC
   * DataSourceTransactionManager. Some JTA providers might support nested
   * transactions as well.
   * @see org.springframework.jdbc.datasource.DataSourceTransactionManager
   */
  NESTED(TransactionDefinition.PROPAGATION_NESTED);


  private final int value;


  Propagation(int value) {
   
    this.value = value;
  }

  public int value() {
   
    return this.value;
  }

}

从源码中我们可以得知,Spring事务默认的事务传播机制是:REQUIRED,我们先对 Propagation类中 Spring事务传播机制进行总结:

  1. PROPAGATION_REQUIRED:要求使用事务,如果当前没有事务,则创建一个新的事务,如果当前存在事务,就加入该事务,该设置是默认也是最常用的设置。
  2. PROPAGATION_SUPPORTS:支持使用事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。‘
  3. PROPAGATION_MANDATORY:强制使用事务,如果当前存在事务,就加入该事务,如果当前不存在事务,则抛出异常。
  4. PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
  5. PROPAGATION_NOT_SUPPORTED:不支持事务,如果当前存在事务,就把当前事务挂起。
  6. PROPAGATION_NEVER:不允许使用事务,如果当前存在事务,则抛出异常。
  7. PROPAGATION_NESTED:内嵌事务,如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。

上述 addUser()方法的事务传播机制是 Propagation.SUPPORTS,也就是当前方法有事务就假如事务,不存在事务则以非事务执行,因为 addUser()方法不存在事务,所以该方法就以非事务执行,因此事务失效。

此案例是典型的 Spring事务传播机制使用错误,我们只需要将 @Transactional(propagation = Propagation.SUPPORTS) 修改成 @Transactional(propagation = Propagation.REQUIRED),事务就可以生效了。

总结

本文列举了日常开发中 Spring事务失效常见的7个主要原因,通过分析我们可以发现,事务失效的大部分原因是对 Spring的运行机制不够了解。或许在开发中大家还会遇到其他形形色色的原因,但不管怎样,只要我们能去认真去分析 Spring事务相关的源码,很多问题就迎刃而解。

学习交流

如果你觉得文章有帮助,请帮忙转发给更多的好友,或关注:猿java,持续输出硬核文章。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
11天前
|
缓存 安全 Java
Spring高手之路26——全方位掌握事务监听器
本文深入探讨了Spring事务监听器的设计与实现,包括通过TransactionSynchronization接口和@TransactionalEventListener注解实现事务监听器的方法,并通过实例详细展示了如何在事务生命周期的不同阶段执行自定义逻辑,提供了实际应用场景中的最佳实践。
35 2
Spring高手之路26——全方位掌握事务监听器
|
4月前
|
安全 Java 数据库
一天十道Java面试题----第四天(线程池复用的原理------>spring事务的实现方式原理以及隔离级别)
这篇文章是关于Java面试题的笔记,涵盖了线程池复用原理、Spring框架基础、AOP和IOC概念、Bean生命周期和作用域、单例Bean的线程安全性、Spring中使用的设计模式、以及Spring事务的实现方式和隔离级别等知识点。
|
5月前
|
Java 关系型数据库 MySQL
Spring 事务失效场景总结
Spring 事务失效场景总结
67 4
|
13天前
|
Java 关系型数据库 数据库
京东面试:聊聊Spring事务?Spring事务的10种失效场景?加入型传播和嵌套型传播有什么区别?
45岁老架构师尼恩分享了Spring事务的核心知识点,包括事务的两种管理方式(编程式和声明式)、@Transactional注解的五大属性(transactionManager、propagation、isolation、timeout、readOnly、rollbackFor)、事务的七种传播行为、事务隔离级别及其与数据库隔离级别的关系,以及Spring事务的10种失效场景。尼恩还强调了面试中如何给出高质量答案,推荐阅读《尼恩Java面试宝典PDF》以提升面试表现。更多技术资料可在公众号【技术自由圈】获取。
|
1月前
|
Java 开发者 Spring
Spring高手之路24——事务类型及传播行为实战指南
本篇文章深入探讨了Spring中的事务管理,特别是事务传播行为(如REQUIRES_NEW和NESTED)的应用与区别。通过详实的示例和优化的时序图,全面解析如何在实际项目中使用这些高级事务控制技巧,以提升开发者的Spring事务管理能力。
48 1
Spring高手之路24——事务类型及传播行为实战指南
|
17天前
|
JavaScript Java 关系型数据库
Spring事务失效的8种场景
本文总结了使用 @Transactional 注解时事务可能失效的几种情况,包括数据库引擎不支持事务、类未被 Spring 管理、方法非 public、自身调用、未配置事务管理器、设置为不支持事务、异常未抛出及异常类型不匹配等。针对这些情况,文章提供了相应的解决建议,帮助开发者排查和解决事务不生效的问题。
|
27天前
|
XML Java 数据库连接
Spring中的事务是如何实现的
Spring中的事务管理机制通过一系列强大的功能和灵活的配置选项,为开发者提供了高效且可靠的事务处理手段。无论是通过注解还是AOP配置,Spring都能轻松实现复杂的事务管理需求。掌握这些工具和最佳实践,能
42 3
|
3月前
|
Java 数据库连接 数据库
spring复习05,spring整合mybatis,声明式事务
这篇文章详细介绍了如何在Spring框架中整合MyBatis以及如何配置声明式事务。主要内容包括:在Maven项目中添加依赖、创建实体类和Mapper接口、配置MyBatis核心配置文件和映射文件、配置数据源、创建sqlSessionFactory和sqlSessionTemplate、实现Mapper接口、配置声明式事务以及测试使用。此外,还解释了声明式事务的传播行为、隔离级别、只读提示和事务超时期间等概念。
spring复习05,spring整合mybatis,声明式事务
|
3月前
|
Java 测试技术 数据库
Spring事务传播机制(最全示例)
在使用Spring框架进行开发时,`service`层的方法通常带有事务。本文详细探讨了Spring事务在多个方法间的传播机制,主要包括7种传播类型:`REQUIRED`、`SUPPORTS`、`MANDATORY`、`REQUIRES_NEW`、`NOT_SUPPORTED`、`NEVER` 和 `NESTED`。通过示例代码和数据库插入测试,逐一展示了每种类型的运作方式。例如,`REQUIRED`表示如果当前存在事务则加入该事务,否则创建新事务;`SUPPORTS`表示如果当前存在事务则加入,否则以非事务方式执行;`MANDATORY`表示必须在现有事务中运行,否则抛出异常;
172 4
Spring事务传播机制(最全示例)
|
2月前
|
Java 程序员 Spring
Spring事务的1道面试题
每次聊起Spring事务,好像很熟悉,又好像很陌生。本篇通过一道面试题和一些实践,来拆解几个Spring事务的常见坑点。
Spring事务的1道面试题