SpringBoot中的声明式事务+切面事务+编程式事务详解
作者:依嘫_吃代码
前言
事务管理对于企业应用来说是至关重要的,当出现异常情况时,它也可以保证数据的一致性。
springBoot中两种事务的实现方式,编程式事务配置和声明式事务配置还有切面事务 还有以后的分布式事务
一、事务特性
- 原子性(Atomicity):
- 事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用;
- 一致性(Consistency):
- 一旦事务完成(不管是成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏;
- 隔离性(Isolation):
- 可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏;
- 持久性(Durability):
- 一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中;
Spring 框架中,涉及到事务管理的 API 大约有100个左右,其中最重要的有三个: TransactionDefinition、PlatformTransactionManager、TransactionStatus
。
所谓事务管理,其实就是”按照给定的事务规则来执行提交或者回滚操作”。”
给定的事务规则”就是用 TransactionDefinition 表示的,”按照……来执行提交或者回滚操作”便是用 PlatformTransactionManager 来表示
而 TransactionStatus 用于表示一个运行着的事务的状态。
打一个不恰当的比喻,TransactionDefinition 与 TransactionStatus 的关系就像程序和进程的关系。
开启事务 @EnableTransactionManagement
二、事务的隔离级别
和数据库中的事务级别是一样的 都会出现事务本身应该有的问题
三、事务的传播行为
四、 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 { }
在方法上
@Override @Transactional(rollbackFor=Exception.class) public void t1(Student one) { }
声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@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;
@Override public final void save2() { transactionTemplate.execute((status)->{ mapper.saveStudent(newOne()); mapper.saveStudent(newOne()); return Boolean.TRUE; }); }
这样两个mapper.saveStudent(newOne());就在一个事务中执行了
3.SpringBoo切面编程式事务
SpringBoo切面编程式事务应该是声明式事务的一种具体体现 此种方式基于AOP功能,所以需要添加
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
配置类
@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()); } }
有了这个切面配置类,就不要用在类或者每个方法上使用@Transactional了,当然方法名前缀要能和设置的匹配上
RuleBasedTransactionAttribute的参数大致和@Transactional的参数相同,里面有详细的注释,就不过多解释了
到此这篇关于SpringBoot中的声明式事务+切面事务+编程式事务详解的文章就介绍到这了,更多相关SpringBoot各类型事务内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!