- 七种传播机制
- 支持当前事务
- 不支持当前事务
- 嵌套事务
七种传播机制
事务传播机制:解决一个事务在多个方法传递的问题
传播机制有以下7种
REQUIRED (默认):如果当前存在事务,则加入该事务,如果不存在事务,则创建一个新事务。
REQUIRES_NEW:无论当前是否存在事务,都会创建一个新事务。如果当前存在事务,会将其挂起。
SUPPORTS:如果当前存在事务,则加入该事务,如果不存在事务,则以非事务的方式执行。
NOT_SUPPORTED:以非事务的方式执行方法,如果当前存在事务,则将其挂起。
MANDATORY:如果当前存在事务,则加入该事务,如果不存在事务,则抛出异常。
NEVER:以非事务的方式执行方法,如果当前存在事务,则抛出异常。
NESTED:如果当前存在事务,则嵌套在该事务中执行;如果不存在事务,则创建一个新事务。嵌套事务是一个独立的子事务,可以单独提交或回滚,但最终必须与外部事务一起提交或回滚。
支持当前事务
那么就涉及到如果方法2/方法3出问题;是全部回滚;还是回滚当前呢?
以第二个方法Method2为示例;进行演示机制1效果(是否真的Method2出现异常;事务全部都回滚;因为传播机制变成一个事务整体)
调用链如下;正常逻辑上是调用日志的Interface;但是我们当前需要显示一下错误的信息;所以才选择调用LogService
创建日志表、用户表:两张表才能演示才效果是不是都回滚。
CREATE TABLE UserInfo (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL,
photo VARCHAR(255),
createtime DATETIME,
updatetime DATETIME,
state INT
);
CREATE TABLE Log (
id INT AUTO_INCREMENT PRIMARY KEY,
timestamp DATETIME NOT NULL,
message TEXT
);
创建实体类对应UserInfo、log
package com.example.demo.entity;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class Log {
private int id;
private LocalDateTime timestamp;
private String message;
}
package com.example.demo.entity;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class UserInfo {
private int id;
private String username;
private String password;
private String photo;
private LocalDateTime createtime;
private LocalDateTime updatetime;
private int state;
}
UserService做两件事情;调用UserMapper和调用LogService(正常逻辑调用LogMapper;这里为了异常演示)
package com.example.demo.service;
import com.example.demo.entity.Log;
import com.example.demo.entity.UserInfo;
import com.example.demo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private LogService logService;
public int del(Integer id) {
return userMapper.del(id);
}
@Transactional(propagation = Propagation.REQUIRED)
public int add(UserInfo userInfo) {
// 给用户表添加用户信息
int addUserResult = userMapper.add(userInfo);
System.out.println("添加用户结果:" + addUserResult);
// 添加日志信息
Log log = new Log();
log.setMessage("添加用户信息");
logService.add(log);
return addUserResult;
}
}
LogService:
package com.example.demo.service;
import com.example.demo.entity.Log;
import com.example.demo.mapper.LogMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
@Service
public class LogService {
@Autowired
private LogMapper logMapper;
@Transactional(propagation = Propagation.REQUIRED)
public int add(Log log) {
int result = logMapper.add(log);
System.out.println("添加日志结果:" + result);
// 回滚操作
int num=10/0;
return result;
}
}
这里的演示还不能用回滚操作来代替算数异常;因为内层是要回滚;外层没感知异常。它们都是一个事务;那就出问题了;报错。
UserCOntroller:
package com.example.demo.controller;
import com.example.demo.entity.UserInfo;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
//演示事务传播机制
@RestController
@RequestMapping("/user3")
public class UserController3 {
@Autowired
private UserService userService;
@RequestMapping("/add")
@Transactional(propagation = Propagation.REQUIRED)
public int add(String username, String password) {
if (null == username || null == password ||
username.equals("") || password.equals("")) return 0;
UserInfo user = new UserInfo();
user.setUsername(username);
user.setPassword(password);
int result = userService.add(user);
// 用户添加操作
return result;
}
}
当形成这样子一个调用链:UserController;UserService;LogService;它们的事务机制都是Propagation.REQUIRED。有事务就加入到事务中。最后执行的结果;添加用户成功了;数据库都回滚成功了。
不支持当前事务
上述代码 @Transactional(propagation = Propagation.REQUIRED)把这里有关的全部都改成requires_new;都会以新的事务去运行。
预期结果:
用户添加成功;日志添加失败;各玩各的;日志的回滚了。因为我们在日志里面弄了个算异常。
实际结果:
整个代码报错了;整个程序都是500;用户的添加那里感知到异常(用户表都添加失败);就会进行回滚。因为没有处理异常;让整个项目都感知到了。
解决方案:单独处理这个异常;或者把我们的代码改成直接回滚操作;而不是算数异常
嵌套事务
独立于外部事务的子事务,它具有自己的保存点,并且可以独立于外部事务进行回滚。如果嵌套事务发生异常并回滚,它将会回滚到自己的保存点,而不影响外部事务。(进行到执行新方法之前的保存点)
在嵌套事务中,如果方法1调用了方法2,则方法1称为外部事务,方法2称为内部事务。
结果:内部回滚;不影响外部回滚