- 事务
- 回顾MySQL事务
- Spring事务实现
- 编程式事务实现:
- 声明式事务
- @Transactional 注解
- 作用范围及名称(value/transactionManager)
- 隔离级别:isolation
- 超时时间:timeout
- 修改只读事务
- 指定异常
- 异常捕获情况
- 事务失效场景
- @Transactional原理
事务
什么是事务?
把一组操作封装成一个执行单元;要么一起成功;要么一起失败。比如转账;账户A:-100 账户B:+100;不能扣了钱却发现没到账。
事务分类:
1:编程式;手动写代码操作事务
2:声明式;利用注解自动开启和提交事务
回顾MySQL事务
把MySQL的索引、事务博客写一下;链接扔这里来。先复习完MySQL事务对这个也有帮助
回顾;你先复习一下;不写博客先;这样子就能省点时间
Spring事务实现
创建Spring boot项目;添加:lombok、mysql、mybatis、Spring web依赖。mybatis配置:保存一下;当要用的时候能方便找到即可
事务操作:通过事务管理器(DataSourceTransactionManager)这个对象操作;而你要得到事务就还得传另一个对象TransactionDefinition
# 配置数据库的连接字符串
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mycnblog?characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=111111
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 设置 Mybatis 的 xml 保存路径
mybatis.mapper-locations=classpath:mybatis/*Mapper.xml
# 配置打印 MyBatis 执行的 SQL
mybatis.configuration.log-impl:org.apache.ibatis.logging.stdout.StdOutImpl
# 配置打印 MyBatis 执行的 SQL
logging.level.com.mybatis.demo=debug
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
</mapper>
编程式事务实现:
事务就是操作数据过程里的一个步骤;所以我们构造这样的一个场景;然后看看是否实现开启事务、提交事务、最后回滚事务功能。
各个部分代码如下:
在Controller中进行try catch异常处理更严谨一些;不然在调用时遇到异常 ;事务就挂在那里也没提交和回滚。出现异常就进行回滚事务。
声明式事务
通过注解实现;在⽅法上添加 @Transactional 注解⽆需⼿动开启事务和提交事务。进⼊⽅法时⾃动开启事务,⽅法执⾏完会⾃动提交事务,如果中途发⽣了没有处理的异常会⾃动回滚事务。(通过简介对比;发现和编程式事务没有对比就没有伤害)
当你测试时在上述代码进行算数异常:int num=10/0;他就会进行回滚事务。
@Transactional 注解
事务回滚和单元测试回滚:
这里事务回滚是发生异常回滚;正常执行则提交事务。单元测试则是执行完方法后一定回滚事务。所以单元测试和正常代码的@Transactional 注解含义不同
作用范围及名称(value/transactionManager)
1:只能应用到public方法;在其它的则不生效
2:修饰类时表明对类中所有public方法生效
参数:
作用一模一样;value和transactionManager描述的是同一个东西
隔离级别:isolation
Spring事务隔离级别5种;多了下面这种;这种以连接数据库的隔离级别为准。mysql默认的是可重复读。(isolation = DEFAULT 表示使用默认的数据库事务隔离级别)
事务级别设置:
或者直接:isolation=-1;还是使用枚举比较不容易出错
超时时间:timeout
负一表示没有超时时间;设置的是3;超时时间就是3秒;方法执行超过3秒就自动回滚操作,谨慎使用。
修改只读事务
1:普通事务(默认的事务传播行为下的一种普遍场景)
2:只读事务;没有隔离级别;可重复读
3:无事务;默认的隔离级别;可重复读
只读事务的隔离级别是可以修改的;可控。无事务是你修改不了隔离级别的。
默认情况下read-only=false(普通事务);当设置read-only=true为只读事务;对于那些读就能满足需求的事务性能就会提升。
指定异常
描述但是一个事情–异常;但是我们一般但是发生异常都回滚
异常捕获情况
@Transactional注解在异常被捕获的情况下;是不会进行事务的回滚。当我们对下面算数异常的代码进行异常处理;事务是不会回滚的,选中这个算数异常的代码ctrl+alt+t。
相当于环绕通知;你代码抛异常;但是被你自己吃掉了这个异常;然后你没有抛异常;环绕通知就感知不到异常;它就正常进行执行代码(Spring知道这个异常;同时也知道你处理了这个异常;既然你已经知道异常且处理了;那么Spring就认为是没有问题;不需要自己再去处理异常)
解决方案:
1:将异常抛出去;让框架感知异常后就会回滚事务
2:在你自己代码处理异常手动回滚事务
事务失效场景
1:非public修饰的方法
2:timeout超时
3:代码中有try/catch
4:调用类内部@Transactional方法
当调用类内部的 @Transactional 修饰的方法时,事务是不会生效的。如下并不会回滚事务
5:数据库不支持事务
@Transactional 本质上给调用的数据库发送了:开始事务、提交事务、回滚事务的指令
如果数据库本身不支持事务,比如 MySQL 中设置了使用 MyISAM 引擎,那么它本身是不支持事务的,这种情况下,即使在程序中添加了 @Transactional 注解,那么依然不会有务的行为,
@Transactional原理
基于AOP实现;AOP ⼜是使⽤动态代理实现的。如果⽬标对象实现了接⼝,默
认情况下会采⽤ JDK 的动态代理,如果⽬标对象没有实现了接⼝,会使⽤ CGLIB 动态代理。
AOP 切面织入:当一个被 @Transactional 注解修饰的方法被调用时,Spring AOP 会在方法执行前后织入事务管理的逻辑。
动态代理:Spring AOP 在运行时动态生成代理对象,将事务管理的逻辑添加到代理对象中。
事务管理器:在 Spring 的配置中需要配置一个事务管理器来管理和协调事务的操作。
注解解析:在启动时,Spring 扫描并解析带有 @Transactional 注解的类和方法,解析注解上的属性如事务的传播行为、隔离级别等。
事务开始和提交:在方法调用之前,事务管理器在数据库连接上开启一个新的事务。方法执行完成后,若无异常抛出,则事务管理器提交事务;否则回滚事务。
生成的代理对象:
事务切面:代理对象包含了事务的管理逻辑,例如事务的开始、提交或回滚等。通过代理对象,只是通过正常调用方法即可。
范围和配置:代理对象可以根据配置定义哪些类或方法需要被代理并应用事务管理。这样可以灵活控制事务的切入点和范围,满足不同场景下的事务需求。
使用环绕通知,Spring 可以在方法调用前开启事务,并在方法调用后根据方法执行情况决定是否提交或回滚事务。