- 七种传播机制
- 支持当前事务
- 不支持当前事务
- 嵌套事务
七种传播机制
事务传播机制:解决一个事务在多个方法传递的问题
传播机制有以下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 {@Autowiredprivate UserMapper userMapper;@Autowiredprivate 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 {@Autowiredprivate 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 {@Autowiredprivate 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称为内部事务。
结果:内部回滚;不影响外部回滚