Spring事务的实现原理

2年前 (2022) 程序员胖胖胖虎阿
168 0 0

目录

      • 一、Spring事务分类
      • 二、Spring事务的三大接口
          • 2.1 PlatformTransationManager接口
          • 2.2 TransactionDefinition接口
          • 2.3 TransactionStatus接口
      • 三、Spring事务嵌套最佳实践
          • 3.1 最佳实践场景一:
          • 3.2 最佳实践场景二:
          • 3.3 最佳实践场景三:
          • 3.4 最佳实践场景四:
          • 3.5 最佳实践场景五:
          • 3.6 最佳实践场景六:
          • 3.7最佳实践场景七:
      • 四、Spring事务失效的场景

一、Spring事务分类

  1. 编程式事务: 如果系统需要明确的事务,并且需要细粒度的控制各个事务的边界,此时建议使用编程式事务
  2. 声明式事务: 如果系统对于事务的控制粒度较为粗糙,则建议使用声明式事务,可以通过注解@Transational实现,原理是利用Spring框架通过AOP代理自动完成开启事务,提交事务,回滚事务。回滚的异常默认是运行时异常,可以通过rollbackFor属性制定回滚的异常类型。

二、Spring事务的三大接口

提示:通过Spring事务的源码可知,Spring不是直接管理事务的,而是提供了多种事务管理器,通过这些事务管理器,Spring将事务委托给了Hibernate、MyBaits、JTA等持久性框架的事务来实现

2.1 PlatformTransationManager接口
public interface PlatformTransactionManager extends TransactionManager {
    /**
    *  获取事务状态
    */
    TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
    /**
    *  提交事务
    */
    void commit(TransactionStatus var1) throws TransactionException;
    /**
    *  回滚事务
    */
    void rollback(TransactionStatus var1) throws TransactionException;
}
2.2 TransactionDefinition接口
public interface TransactionDefinition {
     /**
     *  支持当前事务,若当前没有事务就创建一个新的事务
     */
    int PROPAGATION_REQUIRED = 0;
    /**
     *  如果当前存在事务,则加入该事务,如果当前没有事务,则以非事务的方式继续运行
     */
    int PROPAGATION_SUPPORTS = 1;
    /**
     *  (强制性的)
     *  如果当前存在事务,则加入该事务,如果当前没有事务,则抛出异常
     */
    int PROPAGATION_MANDATORY = 2;
    /**
     *  创建一个新的事务,如果当前存在事务,则把当前事务挂起
     */
    int PROPAGATION_REQUIRES_NEW = 3;
    /**
     * 以非事务方式运行,如果当前存在事务,则把当前事务挂起
     */
    int PROPAGATION_NOT_SUPPORTED = 4;
    /**
     * 以非事务的方式运行,如果当前存在事务,则抛出异常
     */
    int PROPAGATION_NEVER = 5;
    /**
     * (嵌套的)
     * 表示如果当前正有一个事务在运行中,则该方法运行在一个嵌套的事务中,被嵌套的事务可以独立于封装的事务进行提交或者回滚(这里需要事务的保存点),如果封装的事务不存在,后续事务行为同PROPAGATION_REQUIRES_NEW
     */
    int PROPAGATION_NESTED = 6;
    /**
    *  使用后端的默认隔离级别
    */
    int ISOLATION_DEFAULT = -1;
   /**
    * 读未提交
    */
    int ISOLATION_READ_UNCOMMITTED = 1;
    /** 
    * 读已提交
    */
    int ISOLATION_READ_COMMITTED = 2;
    /**
    * 可重复度
    */
    int ISOLATION_REPEATABLE_READ = 4;
    /** 
    * 串行化
    */
    int ISOLATION_SERIALIZABLE = 8;
    int TIMEOUT_DEFAULT = -1;

    default int getPropagationBehavior() {
        return 0;
    }

    default int getIsolationLevel() {
        return -1;
    }

    default int getTimeout() {
        return -1;
    }

    default boolean isReadOnly() {
        return false;
    }

    @Nullable
    default String getName() {
        return null;
    }

    static TransactionDefinition withDefaults() {
        return StaticTransactionDefinition.INSTANCE;
    }
}

2.3 TransactionStatus接口
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
    /**
    *  是否有保存点
    */
    boolean hasSavepoint();
    /**
    *  将事务涉及的数据刷新到磁盘
    */
    void flush();
}

三、Spring事务嵌套最佳实践

我们将此方法作为内部方法,有外部方法调用此方法,我们在方法的最后面故意制造了算术异常来检验以下场景:

 /**
     * 项目延期
     *
     * @param id    工单id
     * @param value 延期天数
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, rollbackFor = Exception.class)
    public Boolean projectDelay(String id, Integer value) {
        EqOverhaulWorkOrderEntity eqOverhaulWorkOrderEntity = eqOverhaulWorkOrderMapper.selectById(id);
        Date startTime = eqOverhaulWorkOrderEntity.getStartTime();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(startTime);
        calendar.add(Calendar.DAY_OF_MONTH, value);
        eqOverhaulWorkOrderEntity.setStartTime(calendar.getTime());
        int update = eqOverhaulWorkOrderMapper.updateById(eqOverhaulWorkOrderEntity);
        int i = 1/0;
        return update == 1;
    }
3.1 最佳实践场景一:

场景一为外部方法无事务注解,内部方法添加REQUIRED事务传播类型:
总结: 内部方法抛出异常,内部方法执行失败,不会影响外部方法的执行,外部方法执行成功。

3.2 最佳实践场景二:

场景二为外部方法添加REQUIRED事务传播类型,内部方法无事务注解。
总结: 内部方法抛出异常,会影响外部方法的执行,导致外部事务的回滚。

3.3 最佳实践场景三:

场景三为外部方法添加REQUIRED事务传播类型,内部方法添加REQUIRED事务传播类型。
总结: 内部方法抛出异常,会影响外部方法的执行,事务回滚。

3.4 最佳实践场景四:

场景四为外部方法添加REQUIRED事务传播类型,内部方法添加NOT_SUPPORTED事务传播类型。
总结: 内部方法抛出异常,外部方法回滚,内部方法不会回滚,外部方法抛出异常,内部方法执行成功,内部方法提交,内部方法抛出异常,也不会回滚。内部方法永远不会回滚。

3.5 最佳实践场景五:

场景五外部方法添加REQUIRED事务传播类型,内部方法添加REQUIRED_NEW事务传播类型。
总结:内部方法抛出异常,内部方法和外部方法都会执行失败,事务回滚。

3.6 最佳实践场景六:

场景六为外部方法添加REQUIRED事务传播类型,内部方法添加REQUIRED_NEW事务传播类型,并且把异常代码移动到外部方法的尾部。
总结: 外部方法抛出异常,外部方法执行失败,事务回滚,内部方法执行成功,内部方法事务提交。

3.7最佳实践场景七:

场景七为外部方法添加REQUIRED,内部方法添加REQUIRED_NEW事务传播类型,并且把异常代码移动到外部方法的尾部,同时外部方法和内部方法在同一个类中。
总结: 外部方法抛出异常, 内部方法和外部方法都会回滚。

四、Spring事务失效的场景

  1. 数据库不支持事务
  2. 事务方法未被Spring管理,也就是事务方法所在的类没有加载到Spring IOC容器中
  3. 方法没有被public修饰
    @Override
    @Transactional(rollbackFor = Exception.class)
    private Boolean projectDelay(String id, Integer value) {
        EqOverhaulWorkOrderEntity eqOverhaulWorkOrderEntity = eqOverhaulWorkOrderMapper.selectById(id);
        Date startTime = eqOverhaulWorkOrderEntity.getStartTime();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(startTime);
        calendar.add(Calendar.DAY_OF_MONTH, value);
        eqOverhaulWorkOrderEntity.setStartTime(calendar.getTime());
        int update = eqOverhaulWorkOrderMapper.updateById(eqOverhaulWorkOrderEntity);
        return update == 1;
    }

因为方法是私有的,加在私有方法上的事务注解不生效。
4. 在同一个类中的方法调用。
提示:举例详情见最佳实践场景七
5. 方法的事务传播类型不支持事务。
提示:举例详情见最佳实践场景四
6. 不正确地捕获异常。
使用了try-catch代码块将异常捕捉了,没有向上抛出异常,事务不会回滚。
7.标注错误的异常类型。
Spring事务默认回滚类型是RuntimeException类型,如果没有制定回滚的类型,抛出的错误不是RuntimeException类型,则无法回滚

版权声明:程序员胖胖胖虎阿 发表于 2022年11月4日 上午8:48。
转载请注明:Spring事务的实现原理 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...