目錄
事務傳播機制
七大事務傳播機制?
支持當前調用鏈上的事務
Propagation.REQUIRED
Propagation.SUPPORTS
Propagation.MANDATORY
不支持當前調用鏈上的事務
Propagation.REQUIRES_NEW
Propagation.NOT_SUPPORTED
Propagation.NEVER
嵌套事務
Propagation.NESTED
前置工作
初始化數據庫
初始化 實體類
初始化 Mapper 接口
初始化 XML 文件
?重點理解部分
NESTED 和 REQUIRED_NEW 的區別
NESTED 和 REQUIRED 的區別
事務傳播機制
- 事務的傳播機制是指在多個事務方法之間調用時,事務如何在這些方法之間傳播
七大事務傳播機制?
支持當前調用鏈上的事務
Propagation.REQUIRED
- 默認的事務傳播級別
如果當前沒有事務,則新建一個事務,如果當前存在事務,則加入該事務
實例理解
Propagation.SUPPORTS
如果當前方法沒有事務,就以非事務方式執行,如果已經存在一個事務中,則加入到這個事務中
Propagation.MANDATORY
如果當前方法沒有事務,則拋出異常,如果已經存在一個事務中,則加入到這個事務中
不支持當前調用鏈上的事務
Propagation.REQUIRES_NEW
創建一個新事務,如果存在當前事務,則掛起當前事務
Propagation.NOT_SUPPORTED
以非事務方式執行,如果存在當前事務,則掛起當前事務
Propagation.NEVER
以非事務方式執行,如果當前事務存在,則拋出異常
嵌套事務
Propagation.NESTED
如果當前存在事務,則在嵌套事務中執行,否則與 REQUIRED 的操作一樣
前置工作
- 此處為了方便下文進行代碼測試理解
- 我們先將準備工作做好
初始化數據庫
- 創建一個 user 表,并添加幾條用戶信息
初始化 實體類
- 創建?User 實體類 與 數據庫的 user 表字段名相對應
import lombok.Data;@Data public class User {private int id;private String name;private int age;private String password;private int state; }
初始化 Mapper 接口
- 初始化?UserMapper 接口,此處我們添加一個 add?方法
import com.example.demo.entity.User; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param;@Mapper public interface UserMapper { // 新增用戶信息Integer add(User user); }
初始化 XML 文件
- 在與 UserMapper 接口相對應的 XML 文件中,添加上與 add 方法 相對應的 sql 語句
<?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"><insert id="add">insert into user(name,age,password) values(#{name},#{age},#{password})</insert> </mapper>
?重點理解部分
NESTED 和 REQUIRED_NEW 的區別
- REQUIRED_NEW 是新建一個事務并且新開始的這個事務與原有事務無關
- 而 NESTED 是當前存在事務時會開啟一個嵌套事務
- 在?NESTED 情況下,父事務回滾時,子事務也會回滾
- 而 REQUIRED_NEW 情況下,原有事務回滾,不會影響新開啟的事務
實例理解
- 我們創建一個 userController 類,并給 addUser?方法加上 REQUIRED 類型的事務
- ?此時的 addUser 方法,將自動創建一個事務A
- 注意我們在 addUser 中加入了一個算數異常
import com.example.demo.entity.User; import com.example.demo.service.LoggerService; 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("/user2") public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/add")@Transactional(propagation = Propagation.REQUIRED)public String addUser(User user) { // 調用 userService 的 add 方法int result = userService.add(user);int c = 10/0;return "add 方法:" + (result == 1 ? "新增用戶成功": "新增用戶失敗");} }
- 可以看到?userController 類中調用了 add 方法,該方法在 userService 中
- 此處我們給 add 方法加上 NESTED 類型的事務
- 因為 addUser 已經創建了一個事務A,所以此處的 add 方法將在 事務A中創建一個嵌套事務
import com.example.demo.entity.User; 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;// 新增用戶信息@Transactional(propagation = Propagation.NESTED)public int add(User user) {int result = userMapper.add(user);return result;} }
運行結果:
- 在瀏覽器中輸入對應的 URL 來訪問 addUser 方法
- 此時我們關注的是 在?NESTED 情況下,父事務回滾時,子事務也會回滾
- 此處正好為 addUser 方法發生了 算數異常,從而進行了回滾操作
- 且在拋出算數異常前,就已經調用了 userService.add 方法
- 如果此處數據庫中插入了新用戶信息,則代表子事務未進行回滾操作
- 此時我們查看 user 表,發現新用戶未插入,即子事務進行了回滾操作,與預期結果一致
調整 add 方法的事務傳播機制
- 我們將 add 方法改為 REQUIRES_NEW?類型的事務
- 因為 addUser 已經創建了一個事務A,所以此處的 add 方法將會把 事務A 掛起,另外創建一個 事務B
import com.example.demo.entity.User; 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;// 新增用戶信息@Transactional(propagation = Propagation.REQUIRES_NEW)public int add(User user) {int result = userMapper.add(user);return result;} }
運行結果:
- 在瀏覽器中輸入對應的 URL 來訪問 addUser 方法
- 此時我們關注的是 REQUIRED_NEW 情況下,原有事務回滾,不會影響新開啟的事務
- 查看 user 表發現新增了一條用戶信息,即新開啟的事務B 未進行回滾,與預期結果一致
NESTED 和 REQUIRED 的區別
- REQUIRED 的情況下,調用方存在事務時,則被調用發和調用方使用同一個事務
- 那么被調用方出現異常時,由于共用同一個事務,所以無論是否 catch 異常,事務都會回滾
- 而在 NESTTED 情況下,被調用方發生異常時,調用方可以 catch 其異常,這樣只有子事務回滾,父事務不回滾
實例理解
- 我們創建一個 userController 類,并給 addUser?方法加上 REQUIRED 類型的事務
- 此時的 addUser 方法,將自動創建一個事務A
- 我們將在 userService 的 add 方法中加入一個 算數異常
- 為了驗證 REQUIRED 的情況下,被調用方出現異常時,由于共用同一個事務,所以無論是否 catch 異常,事務都會回滾
- 如果不捕獲?add 方法拋出的異常 事務A 肯定會進行回滾操作
- 所以我們此處對算數異常進行捕獲,由此來看 事務A 是否還會進行回滾操作
import com.example.demo.entity.User; import com.example.demo.service.LoggerService; 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("/user2") public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/add")@Transactional(propagation = Propagation.REQUIRED)public String addUser(User user) {int result = 0;try { // 調用 userService 的 add 方法result = userService.add(user);}catch (Exception e){e.printStackTrace();}return "add 方法:" + (result == 1 ? "新增用戶成功": "新增用戶失敗");} }
- 此處我們給 add 方法加上?REQUIRED 類型的事務
- 因為 addUser 已經創建了一個事務A,所以此處的 add 方法將會直接加入?事務A 中
- 此處我們在 add 方法中加入了一個算數異常
import com.example.demo.entity.User; 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 注解 代表該類會伴隨著 項目的啟動而注入到容器中 @Service public class UserService {@Autowiredprivate UserMapper userMapper;// 新增用戶信息@Transactional(propagation = Propagation.REQUIRED)public int add(User user) {int result = userMapper.add(user);int a = 1/0;return result;} }
運行結果:
- 在瀏覽器中輸入對應的 URL 來訪問 addUser 方法
- 查看 user 表發現沒有新增新用戶信息,即事務A進行了回滾操作,與預期結果一致
調整 add 方法的事務傳播機制
- 我們將 add 方法改為? NESTED?類型的事務
- 因為 addUser 已經創建了一個事務A,所以此處的 add 方法將會在 事務A 中創建一個嵌套事務
import com.example.demo.entity.User; 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 注解 代表該類會伴隨著 項目的啟動而注入到容器中 @Service public class UserService {@Autowiredprivate UserMapper userMapper;// 新增用戶信息@Transactional(propagation = Propagation.NESTED)public int add(User user) {int result = userMapper.add(user);int a = 1/0;return result;} }
運行結果:
- 在瀏覽器中輸入對應的 URL 來訪問 addUser 方法
- 此處為了驗證 NESTTED 情況下,被調用方發生異常時,調用方可以 catch 其異常,這樣只有子事務回滾,父事務不回滾
- 在瀏覽器中輸入對應的 URL 來訪問 addUser 方法
- 我們可以看到 事務A 未進行回滾操作
- 查看 user 表發現沒有新增新用戶信息,即子事務進行了回滾操作,與預期結果一致