• SpringBoot基础之声明式事务和切面事务和编程式事务




    前言

    事务管理对于企业应用来说是至关重要的,当出现异常情况时,它也可以保证数据的一致性。
    springBoot中两种事务的实现方式,编程式事务配置和声明式事务配置还有切面事务 还有以后的分布式事务

    详情参考 这篇


    一、事务特性

    在这里插入图片描述

    • 原子性(Atomicity):
      事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用;
    • 一致性(Consistency):
      一旦事务完成(不管是成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏;
    • 隔离性(Isolation):
      可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏;
    • 持久性(Durability):
      一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中;

    Spring 框架中,涉及到事务管理的 API 大约有100个左右,其中最重要的有三个:TransactionDefinition、PlatformTransactionManager、TransactionStatus。所谓事务管理,其实就是”按照给定的事务规则来执行提交或者回滚操作”。”给定的事务规则”就是用 TransactionDefinition 表示的,”按照……来执行提交或者回滚操作”便是用 PlatformTransactionManager 来表示,而 TransactionStatus 用于表示一个运行着的事务的状态。打一个不恰当的比喻,TransactionDefinition 与 TransactionStatus 的关系就像程序和进程的关系。

    开启事务

    @EnableTransactionManagement
    
    • 1

    二、事务的隔离级别

    和数据库中的事务级别是一样的 都会出现事务本身应该有的问题
    在这里插入图片描述

    三、事务的传播行为

    在这里插入图片描述

    四、 Springboot事务

    1.Springboot声明式事务

    声明式事务是建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
    Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。

    DataSource、TransactionManager这两部分只是会根据数据访问方式有所变化,比如使用Hibernate进行数据访问时,DataSource实际为SessionFactory,TransactionManager的实现为HibernateTransactionManager。

    根据代理机制的不同,总结了五种Spring事务的配置方式,如下图:

    在这里插入图片描述

    声明式事务@Transactional可以使用在类上,也可以使用在public方法上. 如果是使用在类上,则是对所有的public方法都开启事务,如果类和方法上都有则方法上的事务生效
    在类上

    @Transactional(rollbackFor=Exception.class)
    public class TransactionServiceImpl implements TransactionService {
    }
    
    • 1
    • 2
    • 3

    在方法上

    @Override
    @Transactional(rollbackFor=Exception.class)
    public void t1(Student one) {
    }
    
    • 1
    • 2
    • 3
    • 4

    声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。

    优点:

    编程式事务每次实现都要单独实现,但业务量大且功能复杂时,使用编程性事务无疑是痛苦的;而声明式事务不同,声明式事务属于非侵入性,不会影响业务逻辑的实现,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中;

    非侵入式的开发方式,声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持;

    缺点:

    最细粒度只能是作用到方法级别,无法做到像编程事务那样可以作用到代码块级别;

    实现方式:

    使用拦截器:基于TransactionInterceptor 类来实施声明式事务管理功能(Spring最初提供的实现方式);

    Bean和代理:基于 TransactionProxyFactoryBean的声明式事务管理

    使用tx标签配置的拦截器:基于tx和aop名字空间的xml配置文件(基于Aspectj AOP配置事务);
    全注解:基于@Transactional注解;

    @Transactional的参数

    在这里插入图片描述

    声明式事务的约定流程:

    首先Spring通过事务管理器(PlatformTransactionManager的子类)创建事务,与此同时会把事务定义中的隔离级别、超时时间等属性根据配置内容往事务上设置。而根据传播行为配置采取一种特定的策略,后面会谈到传播行为的使用问题,这是Spring根据配置完成的内容,你只需要配置,无须编码。然后,启动开发者提供的业务代码,我们知道Spring会通过反射的方式调度开发者的业务代码,但是反射的结果可能是正常返回或者产生异常返回,那么它给的约定是只要发生异常,并且符合事务定义类回滚条件的,Spring就会将数据库事务回滚,否则将数据库事务提交,这也是Spring自己完成的
      在这里插入图片描述

    显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。和编程式事务相比,声明式事务唯一不足地方是,它的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。

    声明式事务管理也有两种常用的方式,一种是基于tx和aop名字空间的xml配置文件,另一种就是基于@Transactional注解。显然基于注解的方式更简单易用,更清爽。

    2. Springboot编程式事务

    编程式事务:
    是侵入性事务管理,直接使用底层的PlatformTransactionManager、使用TransactionTemplate(Spring推荐使用);
    编程式事务管理对基于 POJO 的应用来说是唯一选择。我们需要在代码中调用beginTransaction()、commit()、rollback()等事务管理相关的方法;

    @Autowired 
    private TransactionTemplate transactionTemplate;
    
    • 1
    • 2
    @Override
      public final void save2() {
       transactionTemplate.execute((status)->{
                mapper.saveStudent(newOne());
                mapper.saveStudent(newOne());
                return Boolean.TRUE;
            });
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这样两个mapper.saveStudent(newOne());就在一个事务中执行了

    SpringBoo切面编程式事务

    SpringBoo切面编程式事务应该是声明式事务的一种具体体现
    此种方式基于AOP功能,所以需要添加
    详细看这篇

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4

    配置类

    @Aspect
    @Configuration
    public class MyTransactionConfig {
    
        /**
         * 配置方法过期时间,默认-1,永不超时
         */
        private final static int TX_METHOD_TIME_OUT = 10;
    
       /**
         * 全局事务位置配置 在哪些地方需要进行事务处理
         * 配置切入点表达式
         */
        private static final String POITCUT_EXPRESSION = "execution(* zdc.enterprise.service.impl.*.*(..))";
    
        @Autowired
        private PlatformTransactionManager platformTransactionManager;
    
    
        @Bean
        public TransactionInterceptor txadvice() {
    
            /*只读事物、不做更新删除等*/
            /*事务管理规则*/
            RuleBasedTransactionAttribute readOnlyRule = new RuleBasedTransactionAttribute();
            /*设置当前事务是否为只读事务,true为只读*/
            readOnlyRule.setReadOnly(true);
            /* transactiondefinition 定义事务的隔离级别;
             *如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。*/
            readOnlyRule.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);
    
             /*增删改事务规则*/
            RuleBasedTransactionAttribute requireRule = new RuleBasedTransactionAttribute();
            /*抛出异常后执行切点回滚 建议自定义异常*/
            requireRule.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
            /*PROPAGATION_REQUIRED:事务隔离性为1,若当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。 */
            requireRule.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
            /*设置事务失效时间,超过10秒*/
            requireRule.setTimeout(TX_METHOD_TIME_OUT);
    
            /** 配置事务管理规则
             nameMap声明具备需要管理事务的方法名.
             这里使用addTransactionalMethod  使用setNameMap
             */
            Map<String, TransactionAttribute> nameMap = new HashMap<>();
            nameMap.put("add*", requireRule);
            nameMap.put("save*", requireRule);
            nameMap.put("insert*", requireRule);
            nameMap.put("update*", requireRule);
            nameMap.put("delete*", requireRule);
            nameMap.put("remove*", requireRule);
    
            /*进行批量操作时*/
            nameMap.put("batch*", requireRule);
            nameMap.put("get*", readOnlyRule);
            nameMap.put("query*", readOnlyRule);
            nameMap.put("find*", readOnlyRule);
            nameMap.put("select*", readOnlyRule);
            nameMap.put("count*", readOnlyRule);
    
            NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
            source.setNameMap(nameMap);
    
            TransactionInterceptor transactionInterceptor = new TransactionInterceptor(platformTransactionManager, source);
    
            return transactionInterceptor;
        }
    
        /**
         * 设置切面=切点pointcut+通知TxAdvice
         * @return
         */
        @Bean
        public Advisor txAdviceAdvisor() {
            AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
            pointcut.setExpression(POITCUT_EXPRESSION);
            return new DefaultPointcutAdvisor(pointcut, txadvice());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    有了这个切面配置类,就不要用在类或者每个方法上使用@Transactional了,当然方法名前缀要能和设置的匹配上. RuleBasedTransactionAttribute的参数大致和@Transactional的参数相同,里面有详细的注释,就不过多解释了

  • 相关阅读:
    python在线办公自动化oa系统django408
    K8S 中的 CRI、OCI、CRI shim、containerd
    git常用操作-仓库创建、初始化、拉取项目
    C语言每日一题(18)数组匹配
    QT影城网上售票系统
    Spark编程:combineByKey与aggregateByKey异同
    [计算机提升] Windows文件系统类型介绍
    OpenWrt之package: Using Dependencies
    13 Go的错误处理
    Redis
  • 原文地址:https://blog.csdn.net/qq_41810415/article/details/128178595