资料来源:
a. 声明式事务和编程式事务
b. spring事务传播行为详解
c. springboot如何开启事务支持?
我的语雀文档
1.1 概念
为了确保数据的完整性和一致性、避免异常和错误情况,使用事务 将多个操作作为整体 直接执行。即在逻辑上事务是一组操作,要么都执行要么都不执行的“一损俱损”型,事务主要是针对数据库而言的。
1.1.1 事务特性 ACID
- 原子性Atomicity-强调事务的不可分割;
- 一致性Consistency-事务执行前后数据的完整一致性;
- 隔离性 Isolation -事务执行过程不受其他事务的干扰;
3-1不考虑隔离性则会带来的问题
a. 脏读
一个事务读到了另一个事务的未提交的数据 事务可以看到其他事务“尚未提交”的修改。如果另一个事务回滚,那么当前事务读到的数据就是脏数据。
b. 不可重复读
一个事务读到了另一个事务已经提交的 update 的数据导致多次查询结果不一致 在一个事务内,多次读同一数据,在这个事务还没有结束时,如果另一个事务恰好修改了这个数据,那么,在第一个事务中,两次读取的数据就可能不一致。
c. 虚幻读
一个事务读到了另一个事务已经提交的 insert 的数据导致多次查询结果不一致 – 在一个事务中,第一次查询某条记录,发现没有,但是,当试图更新这条不存在的记录时,竟然能成功,并且,再次读取同一条记录,它就神奇地出现。 - 持久性 Durability -事务一旦结束,数据就可以持久到数据库。
- 事务隔离性 – 保证安全
ⅰ. Read uncommitted 三个问题都有
ⅱ. read committed 避免脏读 Oracle 默认
ⅲ. repeatable read 避免脏读和不可重复读 MySQL默认的
ⅳ. Serializable 避免所有问题,事务被处理为顺序执行
1.1.2 事务的传播行为
Spring在TransactionDefinition接口中规定了7种类型的事务传播行为。事务传播行为是Spring框架独有的事务增强特性,他不属于的事务实际提供方数据库行为。这是Spring为我们提供的强大的工具箱,使用事务传播行可以为我们的开发工作提供许多便利。
事务传播行为用来描述 由某一个事务传播行为修饰 的方法例如methodB被嵌套进另一个方法methodA时,事务将如何传播 。and遇到exception会事务回滚。
public void methodA(){
methodB();
//.....
}
//@Transaction(Propagation=XXX)设定事务传播行为;
@Transaction(Propagation=XXX)
public void methodB(){
//.....
}
a. 英文解释: required 需要 supports 支持 mandatory 强制义务的 never 从不 nested内嵌的
b. Propagation.REQUIRED:在外围方法 开启事务 的情况下Propagation.REQUIRED修饰的内部方法会加入到外围方法 的事务中,所有Propagation.REQUIRED修饰的内部方法和外围方法均属于同一事务,只要一个方法回滚,整个事务均回滚; 外部要是没有事务,内部互相独立的方法事务间相互独立,事务互不影响。
注意:调用者和被调用者是否在同一个类中效果不同。 目前的demo是不同类的,catch异常后回滚;然而,如果在同一个类中,catch却不回滚。(在同一个类中是自我调用,事务注解不会生效的,所以只有外围方法的事务,try catch异常当然不会回滚)
c. Propagation.REQUIRES_NEW在外围方法未开启事务的情况下,Propagation.REQUIRES_NEW修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰; 在外围方法开启事务的情况下Propagation.REQUIRES_NEW修饰的内部方法依然会单独开启独立事务,且与外部方法事务也独立,内部方法之间、内部方法和外部方法事务均相互独立,互不干扰。
d. Propagation.NESTED在外围方法未开启事务的情况下Propagation.NESTED和Propagation.REQUIRED 作用相同,修饰的内部方法都会新开启自己的事务,且开启的事务相互独立,互不干扰。在外围方法开启事务的情况下Propagation.NESTED修饰的内部方法属于外部事务的子事务,外围主事务回滚,子事务一定回滚;而内部子事务可以单独回滚而不影响外围主事务和其他子事务(需要抛出子事务的异常,否则外部事务感知到子事务异常也会回滚)。
e. PROPAGATION_MANDATORY、PROPAGATION_SUPPORTS等剩下的不常用,了解。
f. 事务不生效举例:
ⅰ. MySQL 5.5.5 开始的默认存储引擎是:InnoDB,之前默认的都是:MyISAM,MyISAM 引擎是不支持事务操作的,InnoDB 才是支持事务的引擎,一般要支持事务都会使用 InnoDB。
ⅱ. 不被Spring管理:@Service 注解注释掉,这个类不会被加载成 Bean,那这个类就不会被 Spring 管理了,事务自然就失效。
ⅲ. 出现异常不抛出来,感知不到异常,不进行事务回滚。
ⅳ. 默认回滚是 RuntimeException ,而抛出其他类型错误又不在注解上配置 事务就不会生效。 @Transactional(rollbackFor = Exception.class)
1.2 Spring事务
SpringBoot框架中使用 声明式事务 和 编程式事务来管理事务处理,声明式更常见;当一个事务所允许执行的最长时间,如果在超时时间内还没有完成的话,就自动回滚 ,这称之为事务超时; 给方法加上了 @Transactional 注解,那这个方法中所有的 SQL 都会放在一个事务里,否则,每条 SQL 都会单独开启一个事务,中间被其他事务修改了数据,都会实时读取到,造成不可重复读。
1.2.1 开启使用
在启动类上面加上注解:@EnableTransactionManagement,但是在springboot的自动配置机制下,如果已经引用了starter-jdbc等,会自动向容器中添加事务管理器,所以允许 不 在主类上标注
@EnableTransactionManagement,
package wsn;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
@SpringBootApplication
@Configurable
@EnableTransactionManagement
public class Application {
public static void main(String[] args){
SpringApplication.run(Application.class);
}
}
1.2.2 声明式事务
1. 声明式事务本质
通过AOP实现,本质是对方法前后拦截,在方法开始前创建/加入一个事务,执行完方法后根据执行情况进行提交或者回滚。可以理解为将方法中的所有行为操作都整合到一个事务中,全部成功返回提交commit 否则 rollback_only 进行数据的提交或者回滚。
public class TransactionInterceptor extends TransactionAspectSupport
implements MethodInterceptor, Serializable{}
包括:
1-a. 启动时扫描@Transactional注解:在启动时,Spring Boot会扫描所有使用了@Transactional注解的方法,并将其封装成TransactionAnnotationParser对象。
annotation :注解,解释;
1-b. AOP 来实现事务管理的核心类依然是 TransactionInterceptor。TransactionInterceptor 是一个拦截器,用于拦截使用了 @Transactional 注解的方法。
intercept : 拦截,截住; intercepter/interceptor : 拦截器,
1-c. 将TransactionInterceptor 织入到目标方法中:在AOP编程中,使用AspectJ编写切面类,通过@Around注解将TransactionInterceptor织入到目标方法中。
1-d. 在目标方法执行前创建事务:在目标方法执行前,TransactionInterceptor 会调用 PlatformTransactionManager 创建一个新的事务,并将其纳入到当前线程的事务上下文中。
1-e. 执行目标方法:在目标方法执行时,如果发生异常,则将事务状态标记为ROLLBACK_ONLY;否则,将事务状态标记为COMMIT。
1-f. 提交或回滚事务:在目标方法执行完成后,TransactionInterceptor会根据事务状态(COMMIT或ROLLBACK_ONLY)来决定是否提交或回滚事务。
2 优缺点
2-1 优点:
○ 简化代码:开发人员只需要关注业务逻辑,而无需手动管理事务,可以减少代码复杂度和工作量。
○ 可配置性强:事务属性可以通过XML文件、注解等方式进行配置,灵活方便
○ 易于扩展:可以通过AOP技术轻松地扩展使其支持新的事务策略。
2-2 缺点:
○ 限制较大:事务属性需要在方法或类级别进行声明,这可能会导致某些情况下难以满足特定的业务需求。
○ 难以调试:由于事务是在AOP层面进行管理的,因此在调试时可能难以追踪事务管理的具体细节。
3 事务管理模型
事务管理的核心抽奖为 TransactionManager ,源码只是一个标记接口
public interface TransactionManager {}
然后 该接口有两个子接口,分别是编程式事务接口 ReactiveTransactionManager 和声明式事务接口 PlatformTransactionManager。 PlatformTransactionManager为各个平台如 Hibernate (HibernateTransactionManager) 都提供了对应的事务管理器,
interface PlatformTransactionManager extends TransactionManager{
// TransactionStatus 主要用来存储当前事务的一些状态和数据,
// 比如说事务资源(connection)、回滚状态等
// 根据事务定义获取事务状态
TransactionStatus getTransaction(TransactionDefinition definition)
throws TransactionException;
// 提交事务
void commit(TransactionStatus status) throws TransactionException;
// 事务回滚
void rollback(TransactionStatus status) throws TransactionException;
}
1.2.3 编程式事务
**
1 编程式事务通过具体编码实现
public class TransactionTemplate extends DefaultTransactionDefinition
implements TransactionOperations, InitializingBean{}
TransactionTemplate类的execute()方法封装了事务的具体实现,通过调用TransactionCallback对象的doInTransaction()方法来执行业务逻辑并管理事务。在具体实现中,TransactionTemplate类会自动控制事务的提交和回滚,并将异常抛出给上层调用者进行处理。
2 优缺点
2-1 优点:
○ 灵活性强:开发人员可以在代码中根据具体业务需要来控制事务的具体范围和属性。
○ 易于调试:由于事务管理在代码层面上实现,因此开发人员可以很容易地追踪事务管理的细节。
2-2 缺点:
○ 代码复杂度高:需要在代码中手动处理事务,并处理各种异常情况,可能会增加代码的复杂度和工作量。
○ 可配置性差:事务的范围和属性需要在代码中显式声明,这可能会导致一些特定的业务需求难以满足
1.2.4 二者区别
- 技术实现方式:声明式事务是通过AOP技术来实现的,而编程式事务是通过编写具体的代码来实现的。
- 代码耦合度:声明式事务可以将事务处理逻辑从业务代码中分离出来,从而降低代码的耦合度。而编程式事务需要在业务代码中显式地调用事务管理代码,因此会增加代码的耦合度。
- 难易程度:声明式事务相对来说比较容易上手,开发人员只需要学习注解或XML配置即可。而编程式事务需要开发人员理解事务管理的底层机制,并编写具体的代码。
- 性能影响:由于声明式事务是由容器来处理的,所以在一些场景下可能会对性能产生影响, 大事务 会有很多问题(下面在说一下大事务出现的问题)。而编程式事务由于直接调用事务管理API,相对来说会有更好的性能表现。
- 声明式事务虽然优于编程式事务,但也有不足,声明式事务管理的粒度是方法级别,而编程式事务是可以精确到代码块级别的。
总体而言,声明式事务和编程式事务都有各自的优缺点,开发人员需要根据具体需求选择适合的方式来控制事务。
大事务时间过长可能会导致以下问题:
1 数据库锁定:当事务涉及到大量的数据操作时,事务可能会占用数据库资源并长时间锁定相关数据。这可能会导致其他事务无法访问或修改这些数据,从而降低系统的并发性能和吞吐量。
2 资源耗尽:长时间运行的事务需要占用更多的系统资源,如内存和CPU等。如果系统资源不足,可能会导致系统出现延迟、死锁等问题,甚至导致系统崩溃。
3 事务失败概率增加:当事务时间过长时,事务执行期间可能会发生各种错误,如网络故障、硬件故障、操作系统问题等。此时,事务可能无法成功提交,导致数据丢失或数据不一致。
4 应用程序超时:应用程序通常会为每个事务设置一个超时时间,以避免事务持续时间过长。如果事务持续时间超过设定的超时时间,则应用程序可能会因为等待事务完成而阻塞,最终导致应用程序崩溃或超时。
5 回滚时间增加:如果事务失败需要回滚,长时间运行的事务将需要更长的时间来进行回滚操作。这可能会导致数据不一致或丢失,并增加数据库维护的工作量。
因此,开发人员应该尽量避免事务时间过长,合理地设置事务范围、优化事务操作方式以及减少数据访问次数等措施,以提高系统的并发性能和吞吐量。
1.2.5 事务回滚
默认情况下,事务只在出现 运行时异常(Runtime Exception)时回滚。
如果想要回滚特定的异常类型,可以在注解中设置,例如
@Transactional(rollbackFor= MyException.class)
1.2.6 @Transactional注解
来自spring框架的事务包下的注解包;
- 避免同一个类中调用 @Transactional 注解的方法,这样会导致事务失效。
- @Transactional 注解只能回滚非检查型异常,具体为RuntimeException及其子类和Error子类。
- @Transactional注解不能回滚被try{}catch() 捕获的异常,但是可以抛出来throw e 就可以被感知到。
- @Target({ElementType.METHOD, ElementType.TYPE})说明可以用在方法和类上;
a. 在 public 方法上使用, 如果目标方法不是public,则TransactionAttribute返回null,即不支持事务。
b. 作用于类上时,该类的所有 public 方法将都具有该类型的事务属性 - value 和 transactionManager是一样的意思。当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器。事务管理模型 见 1.2.2声明式事务 的 3 事务管理模型 .
- propagation :传播 isolation:隔离 ;二者均见1.1 概念
● timeout 事务的超时时间,默认值为-1(不设置)。如果超过该时间限制但事务还没有完成,则自动回滚事务
● readOnly 指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true
● rollbackFor 用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。
● noRollbackFor 抛出指定的异常类型,不回滚事务,也可以指定多个异常类型
等待随时补充
…