文章目录
- 前言
- 一、事务特性
- 开启事务
- 二、事务的隔离级别
- 三、事务的传播行为
- 四、 Springboot事务
-
- 1.Springboot声明式事务
-
- 优点:
- 缺点:
- 实现方式:
- @Transactional的参数
- 声明式事务的约定流程:
- 2. Springboot编程式事务
- SpringBoo切面编程式事务
前言
事务管理对于企业应用来说是至关重要的,当出现异常情况时,它也可以保证数据的一致性。
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());就在一个事务中执行了
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
*/
MapString, 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的参数相同,里面有详细的注释,就不过多解释了