目录
Spring 中事务的实现
编程式事务(了解即可):
编程式事务需要自己手动写代码操作事务
这里用用户注册来进行演示:
Spring ⼿动操作事务和上⾯ MySQL 操作事务类似, 有 3 个重要操作步骤:
• 开启事务(获取事务)
• 提交事务
• 回滚事务
package com.suli.springtransdemo.Controller;
import com.suli.springtransdemo.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.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
public class UserController {
@Autowired
private UserService userService;
// JDBC 事务管理器
@Autowired
private DataSourceTransactionManager dataSourceTransactionManager;
//定义事务的属性
@Autowired
private TransactionDefinition definition;
@RequestMapping("registry")
public String registry(String name, String password){
/**
* 1. 开启事务
* 2. 数据操作
* 3. 事务提交/回滚
*/
//1. 开启事务
TransactionStatus transaction = dataSourceTransactionManager.getTransaction(definition);
//用户注册
userService.registryUser(name,password);
log.info("用户注册成功");
//事务提交
// dataSourceTransactionManager.commit(transaction);
//事务回滚
dataSourceTransactionManager.rollback(transaction);
return "注册成功";
}
}
声明式事务(利用注解自动开启和提交事务):
1.首先要添加依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
2.要使用@Transactional注解,无需手动开启事务和提交事
务, 进入方法时自动开启事务, 方法执行完会自动提交事务,如果中途发生了没有处理的异常会自动
回滚事务.
@Slf4j
@RestController
public class UserController2 {
@Autowired
private UserService userService;
@Autowired
private LogInfoService logInfoService;
@Transactional
@RequestMapping("registry1")
public String registry1(String name, String password){
//用户注册
Integer result = userService.registryUser(name,password);
log.info("用户注册成功,影响行数"+ result);
return "注册成功";
}
}
事务回滚注意事项:
如果程序发生Error和IoException和RuntimeException这几个异常时,事务是不会自动进行回滚的
/**
* 抛出,RuntimeException不会自动回滚
* @param name
* @param password
* @return
*/
@Transactional
@RequestMapping("registry2")
public String registry(String name, String password){
//用户注册
Integer result = userService.registryUser(name,password);
log.info("用户注册成功,影响行数"+ result);
try {
int a = 10/0;
}catch (Exception e){
log.error("程序发生异常");
throw new RuntimeException(e);
//手动回滚
// TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
return "注册成功";
}
/**
* 抛出IOException异常,事务没有自动进行回滚
* @param name
* @param password
* @return
*/
@Transactional
@RequestMapping("registry3")
public String registry3(String name, String password) throws IOException {
//用户注册
Integer result = userService.registryUser(name,password);
log.info("用户注册成功,影响行数"+ result);
try {
int a = 10/0;
}catch (Exception e){
log.error("程序发生异常");
throw new IOException(e);
}
return "注册成功";
}
结果观察:
看数据库:
Spring 事务传播机制:
Spring的事务的隔离级别和Mysql还是一样的,由于Mysql上没有方法调来调去,但是我们编程时会有方法上的调用可以不同方法没有用同一个事务会有嵌套等等情况,所以我们的事务还有传播机制!
事务主要有7中传播机制,接下来比较重要的我会用代码来解释一下
我会用一个Controller调用两个service里的方法来进行说明
1.Propagation.REQUIRED:
默认的事务传播级别,如果存在一个事务我们就用这个事务,如果没有事务就创建一个事务,大家一起用
先看没有异常的情况:
请求:
结果两个表都已提交,就是用同一个事务
发生异常:全部失败
7.Propagation.NESTED:
如果当前存在事务则创建新事务作为嵌套事务来运行,如果不存在事务则等价于Propagation.REQUIRED
没有异常情况:
结果:和情况一一样都得到提交
Propagation.NESTED和1.Propagation.REQUIRED的区别:
Propagation.NESTED的事务之间是父子关系,子事务发生异常会影响父事务,而Propagation.REQUIRED则不是
Propagation.NESTED情况下,当子事务发生异常,然后处理了(手动回滚事务)后就不会影响父事务和其他子事务:
结果发生异常的事务没有提交,但是没有发生异常的事务得到提交!
4.Propagation.REQUIRES_NEW:
如果当前存在有事务,就把这个事务挂起不用,自己创建新事务,事务之间相互独立,互不干扰
结果:事务之间互不影响,一个事务提交成功,一个失败
6.Propagation.NEVER:
以非事务的方式运行,如果当前存在事务则抛出异常
结果:直接抛出异常,自然两个都没有提交