文章目录
- 事务
-
- 使用Spring实现事务
-
- [Spring 编程式事务(了解)](#Spring 编程式事务(了解))
- [Spring 声明式事务 @Transactional](#Spring 声明式事务 @Transactional)
- @Transactional
- 总结
事务
- 事务:同时执行多个操作,要么同时成功,要么同时失败
事务具有原子性
比如:转账

- 数据库执行事务


使用Spring实现事务
-
Spring对于事务的实现
(1) 编程式实现(和数据库是一样的)
手动开启事务
手动提交事务/手动回滚事务
(2) 声明式事务(利用自动开启事务和提交事务),推荐使用这种方式
通过注解来实现
-
对事务的操作,必然要使用数据库
-
MyBatis版本不匹配

-
如果引入了MyBatis的依赖,就需要配置数据库
引入MyBatis依赖之所以感觉"需要"配置数据库,是因为Spring Boot的自动配置机制被触发,它试图为你创建一个完整可用的MyBatis环境,而这个环境的基石------数据源(DataSource)------必须由你通过配置文件来提供必要的信息。
没有配置,自动配置就无法完成它的工作,从而导致应用启动失败。

java
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/trans_test?characterEncoding=utf8&useSSL=false
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
configuration: # 配置打印 MyBatis⽇志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true #配置驼峰⾃动转换
Spring 编程式事务(了解)
-
Spring 手动操作事务和上面 MySQL 操作事务类似, 有 3 个重要操作步骤:
• 开启事务(获取事务)
• 提交事务
• 回滚事务
-
SpringBoot 内置了两个对象:
(1) DataSourceTransactionManager 事务管理器. 用来获取事务(开启事务), 提交或回滚事务
(2) TransactionDefinition 是事务的属性, 在获取事务的时候需要将TransactionDefinition 传递进去从而获得一个事务 TransactionStatus

-
观察事务回滚和提交时,日志的差异

Spring 声明式事务 @Transactional
- @Transactional 自动完成事务的提交,回滚,关闭等操作
- @Transactional 什么时候回滚??

@Transactional只会检查程序是否出错了,出错了就回滚,
没有出错或者是捕获了异常就不回滚,
捕获了异常,但是重新抛出了会回滚,
捕获了异常,手动回滚
@Transactional只关心你是否解决掉异常了,不关注你的内部代码,没有解决掉异常就会回滚
java
package com.example.trans.demos.controller;
import com.example.trans.demos.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.beans.Transient;
@Slf4j
@RequestMapping("/trans")
@RestController
public class TranController {
@Autowired
private UserService userService;
// 正常处理,没有错误
@Transactional
@RequestMapping("/registry")
public String registry(String userName,String password){
Integer result = userService.insertUser(userName,password);
log.info("用户插入成功,result " + result);
return "注册成功";
}
// 有异常,没有处理
@Transactional
@RequestMapping("/t2")
public String registry3(String userName,String password){
Integer result = userService.insertUser(userName,password);
int a = 10 / 0;
log.info("用户插入成功,result " + result);
return "注册成功";
}
// 抛出异常并且捕获了异常
@Transactional
@RequestMapping("/t3")
public String registry2(String userName,String password) {
Integer result = userService.insertUser(userName, password);
try {
int a = 10 / 0;
} catch (Exception e){
log.info("程序出错");
}
log.info("用户插入成功,result " + result);
return "注册成功";
}
// 程序报错,重新抛出异常
// 事务回滚
@Transactional
@RequestMapping("/t4")
public String registry4(String userName,String password) {
Integer result = userService.insertUser(userName, password);
try {
int a = 10 / 0;
} catch (Exception e){
log.info("程序出错");
throw e;
}
log.info("用户插入成功,result " + result);
return "注册成功";
}
// 程序报错
// 手动回滚
@Transactional
@RequestMapping("/t5")
public String registry5(String userName,String password) {
Integer result = userService.insertUser(userName, password);
try {
int a = 10 / 0;
} catch (Exception e){
log.info("程序出错");
// 当前事务回滚
// 当前的事务的状态设置为回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
log.info("用户插入成功,result " + result);
return "注册成功";
}
}
@Transactional
- @Transactional 默认处理error和运行时异常才会回滚,遇到非运行时异常就会提交

-
遇到了异常,并且处理了这个异常,但是抛出了其他异常

-
@Transactional可以设置哪些异常去回滚

-
@Transactional的底层:

事务的隔离级别

- 读已提交

- 可重复读

Spring的事务隔离级别
Spring 中事务隔离级别有5 种:
- Isolation.DEFAULT : 以连接的数据库的事务隔离级别为主.
- Isolation.READ_UNCOMMITTED : 读未提交, 对应SQL标准中 READ UNCOMMITTED
- Isolation.READ_COMMITTED : 读已提交,对应SQL标准中 READ COMMITTED
- Isolation.REPEATABLE_READ : 可重复读, 对应SQL标准中 REPEATABLE READ
- Isolation.SERIALIZABLE : 串行化, 对应SQL标准中 SERIALIZABLE
可以自己设置隔离级别:
Spring 中事务隔离级别可以通过 @Transactional 中的 isolation 属性进行设置
java
@Transactional(isolation = Isolation.READ_COMMITTED)
@RequestMapping("/r3")
public String r3(String name,String password) throws IOException {
//... 代码省略
return "r3";
}
事务传播机制
- 事务传播机制: 多个事务方法存在调用关系时, 事务是如何在这些方法间进行传播的
比如:
有两个方法A, B都被 @Transactional 修饰, A方法调用B方法
A方法运行时, 会开启一个事务。 当A调用B时, B方法本身也有事务, 此时B方法运行时, 是加入A的事务, 还是创建⼀个新的事务呢?
这个就涉及到了事务的传播机制。
- 事务的传播机制:


举个例子:
比如有人结婚了,需要买一套房子

事务传播机制的使用
-
REQUIRED(默认值)
-
REQUIRES_NEW
-
Required 调用的事务,如果成功,整体就成功了,如果其中一个失败,整体失败
如果当前存在事务,就加入事务。如果当前没有事务,则创建一个新的事务

-
Required_new

-
嵌套事务

-
nested和required的区别:

总结
