1.介绍
参考:
对于mysql本质上是利用数据库回滚,表的引擎要InnoDB
使用各个版本如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<!-- 打包配置 -->
<packaging>jar</packaging>
<properties>
<java.version>1.8</java.version>
<mysql-driver.version>8.0.28</mysql-driver.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 热部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!-- thymeleaf,for html,jsp... -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- json -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.79</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 数据库连接池druid -->
<!-- https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!--
redis
springboot 2.x:默认用lettuce实现的
springboot 1.x:默认用Jedis实现的
springboot 2.x改用jedis,如下代码
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
排除lettuce包
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
添加jedis客户端
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- redis使用pool -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<!-- <version>2.11.1</version>-->
</dependency>
</dependencies>
<build>
<plugins>
<!--
运用SpringBoot 插件
使用spring-boot-devtools模块的应用,
当classpath中的文件有改变时,会自动重启!
-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<!-- 不加版本号,会报红 -->
<version>2.6.3</version>
</plugin>
<!-- mybatis-generator.start -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.5</version>
<dependencies>
<dependency>
<groupId> mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-driver.version}</version>
<scope>runtime</scope>
</dependency>
<!--
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.5</version>
</dependency>
-->
</dependencies>
<!--
<executions>
<execution>
<id>Generate MyBatis Artifacts</id>
<phase>package</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
-->
<configuration>
<!--允许移动生成的文件 -->
<verbose>true</verbose>
<!-- 是否覆盖 -->
<!-- 此处要特别注意,如果不加这个设置会导致每次运行都会在原目录再次创建-->
<overwrite>true</overwrite>
<!-- generator 工具配置文件的位置 -->
<configurationFile>
src/main/resources/mybatis-generator.xml
</configurationFile>
</configuration>
</plugin>
<!-- mybatis-generator.end -->
</plugins>
</build>
</project>
2.默认情况,事务会在unchecked异常时回滚
2.1注意抛出异常要在@Transactional注解的类/方法中及其子方法
2.1.1Service
package com.demo.services;
import com.demo.models.mapper.AppadminMapper;
import com.demo.models.mapper.UserMapper;
import com.demo.models.po.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class UserService {
//自动注入
@Autowired
private UserMapper userMapper;
public User getUser(Integer id) {
return userMapper.selectByPrimaryKey(id);
}
public int insertUser(User user) throws Exception {
int count = userMapper.insert(user);
if( count > 0){
//这里抛出unchecked异常,会导致回滚
throw new RuntimeException("测试uncheck异常导致回滚");
}
return count;
}
@Transactional
public int insertUsers(List<User> userList) throws Exception {
int count = 0;
for(User user : userList){
//调用子方法insertUser,将使用注解@Transactional定义的传播行为
//insertUser方法没有定义传播行为,它会采用REQUIRED(默认)
//也就是沿用当前的事务,所以它将与insertUsers方法使用同一个事务
count += insertUser(user);
}
return count;
}
}
2.1.2控制器
package com.demo.controllers;
import com.demo.models.po.User;
import com.demo.services.UserService;
import com.demo.utils.AjaxResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
import java.util.HashMap;
@RestController
@RequestMapping("/user")
public class UserController {
//自动注入
@Autowired
private UserService service;
/**
* http://localhost:9000/demo/user/addlist
* @param user
* @return
*/
@RequestMapping(value = "/addlist", method = RequestMethod.POST)
public AjaxResult insertUsers(User user){
try{
int count = service.insertUsers(Arrays.asList(user));
return AjaxResult.success("插入成功", new HashMap<String, Object>(){
{
put("count", count);
put("user", user);
}
});
}catch(Exception e){
return AjaxResult.error(e.getMessage());
}
}
}
2.1.3测试类
package com.demo.controllers;
import com.alibaba.fastjson.JSON;
import com.demo.models.po.User;
import com.example.demo.DemoApplication;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.context.WebApplicationContext;
import javax.servlet.http.Cookie;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.*;
/**
* 想要执行的时候,鼠标放在对应的方法,右键选择run该方法即可。
*/
@RunWith(SpringRunner.class)
//不写classes会报错:you need to use @ContextConfiguration or @SpringBootTest(classes=...) with your test
//classes指向启动类
@SpringBootTest(classes= DemoApplication.class)
public class UserControllerTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mvc;
private MockHttpSession session;
/**
* 开始前,模拟登录
*/
@Before
public void setupMockMvc(){
//初始化MockMvc对象
mvc = MockMvcBuilders.webAppContextSetup(wac).build();
session = new MockHttpSession();
//拦截器那边会判断用户是否登录,所以这里注入一个用户
session.setAttribute("user", "root");
}
/**
* 注意url:这里url不必加上/demo(配置文件的二级目录server.servlet.context-path=/demo)
* @throws Exception
*/
@Test
public void insertUser() throws Exception {
MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
params.add("id", "4");
params.add("name", "test4");
params.add("birthday", "2020-1-4");
params.add("address", "测试城市4");
String uri = "/user/addlist";
String result = mvc.perform(MockMvcRequestBuilders.post(uri)
//模拟cookie
.cookie( new Cookie("token", "123456") )
//模拟session
.session(session)
//主流浏览器如谷歌符合正常规范,不需要设置字符编码了,但考虑非主流浏览器,仍然保留
.contentType(MediaType.APPLICATION_JSON_UTF8)
.accept(MediaType.APPLICATION_JSON_UTF8)
.params(params)
// .contentType(MediaType.APPLICATION_JSON)
// .content(paramsJSON) //传过去为Null????
)
.andExpect(MockMvcResultMatchers.status().is2xxSuccessful())
.andDo(MockMvcResultHandlers.print())
//返回请求的字符串信息
.andReturn().getResponse().getContentAsString();
Assert.assertEquals("调用成功","demo8-1",result);
}
}
结果:回滚
2.2抛出异常不在@Transactional注解的类/方法中及其子方法
如果@Transactional在Service类,而在控制器抛出异常(不管是不是unchecked)都不会回滚
2.2.1控制器类
package com.demo.controllers;
import com.demo.models.po.User;
import com.demo.services.UserService;
import com.demo.utils.AjaxResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
import java.util.HashMap;
@RestController
@RequestMapping("/user")
public class UserController {
//自动注入
@Autowired
private UserService service;
/**
* http://localhost:9000/demo/user/addlist
* @param user
* @return
*/
@RequestMapping(value = "/addlist", method = RequestMethod.POST)
public AjaxResult insertUsers(User user){
try{
int count = service.insertUsers(Arrays.asList(user));
if( count>0 ){
//不在注解方法及其子方法抛出异常,不会导致回滚
throw new RuntimeException("测试uncheck异常不会导致回滚");
}
return AjaxResult.success("插入成功", new HashMap<String, Object>(){
{
put("count", count);
put("user", user);
}
});
}catch(Exception e){
return AjaxResult.error(e.getMessage());
}
}
}
2.2.2Service
package com.demo.services;
import com.demo.models.mapper.AppadminMapper;
import com.demo.models.mapper.UserMapper;
import com.demo.models.po.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class UserService {
//自动注入
@Autowired
private UserMapper userMapper;
public User getUser(Integer id) {
return userMapper.selectByPrimaryKey(id);
}
public int insertUser(User user) throws Exception {
int count = userMapper.insert(user);
return count;
}
@Transactional
public int insertUsers(List<User> userList) throws Exception {
int count = 0;
for(User user : userList){
//调用子方法insertUser,将使用注解@Transactional定义的传播行为
//1.insertUser方法没有定义传播行为,它会采用REQUIRED(默认)
//也就是沿用当前的事务,所以它将与insertUsers方法使用同一个事务
count += insertUser(user);
}
return count;
}
}
结果:不会回滚
3.默认情况下,遇到checked异常,则不会回滚
3.1Service
package com.demo.services;
import com.demo.models.mapper.AppadminMapper;
import com.demo.models.mapper.UserMapper;
import com.demo.models.po.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class UserService {
//自动注入
@Autowired
private UserMapper userMapper;
public User getUser(Integer id) {
return userMapper.selectByPrimaryKey(id);
}
public int insertUser(User user) throws Exception {
int count = userMapper.insert(user);
if( count > 0){
//check异常,不会导致回滚
throw new Exception("测试check异常不会导致回滚");
}
return count;
}
@Transactional
public int insertUsers(List<User> userList) throws Exception {
int count = 0;
for(User user : userList){
//调用子方法insertUser,将使用注解@Transactional定义的传播行为
//insertUser方法没有定义传播行为,它会采用REQUIRED(默认)
//也就是沿用当前的事务,所以它将与insertUsers方法使用同一个事务
count += insertUser(user);
}
return count;
}
}
3.2控制器
package com.demo.controllers;
import com.demo.models.po.User;
import com.demo.services.UserService;
import com.demo.utils.AjaxResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
import java.util.HashMap;
@RestController
@RequestMapping("/user")
public class UserController {
//自动注入
@Autowired
private UserService service;
/**
* http://localhost:9000/demo/user/addlist
* @param user
* @return
*/
@RequestMapping(value = "/addlist", method = RequestMethod.POST)
public AjaxResult insertUsers(User user){
try{
int count = service.insertUsers(Arrays.asList(user));
return AjaxResult.success("插入成功", new HashMap<String, Object>(){
{
put("count", count);
put("user", user);
}
});
}catch(Exception e){
return AjaxResult.error(e.getMessage());
}
}
}
结果:不会回滚
3.3解决方法1:设置回滚点
在@Transactional的类/方法及其子方法中设置回滚点,捕获checked异常后就回滚到回滚点
Service:
package com.demo.services;
import com.demo.models.mapper.AppadminMapper;
import com.demo.models.mapper.UserMapper;
import com.demo.models.po.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import java.util.List;
@Service
public class UserService {
//自动注入
@Autowired
private UserMapper userMapper;
public User getUser(Integer id) {
return userMapper.selectByPrimaryKey(id);
}
public int insertUser(User user) throws Exception {
//插入不会回滚的id=5
User noRollUser = new User();
noRollUser.setId(5);
noRollUser.setName("测试5");
userMapper.insert(noRollUser);
//设置回滚点
Object savePoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint();
try{
int count = userMapper.insert(user);
if( count > 0){
// throw new RuntimeException("测试uncheck异常导致回滚");
throw new Exception("测试check异常不会导致回滚");
}
return count;
}catch(Exception e){
//捕获异常后,回滚到savePoint
TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint);
throw e;
}
}
@Transactional
public int insertUsers(List<User> userList) throws Exception {
int count = 0;
for(User user : userList){
//调用子方法insertUser,将使用注解@Transactional定义的传播行为
//insertUser方法没有定义传播行为,它会采用REQUIRED(默认)
//也就是沿用当前的事务,所以它将与insertUsers方法使用同一个事务
count += insertUser(user);
}
return count;
}
}
结果:只回滚了4,没回滚5
3.4如果是unchecked异常,也会到回滚点,而不是全部
package com.demo.services;
import com.demo.models.mapper.AppadminMapper;
import com.demo.models.mapper.UserMapper;
import com.demo.models.po.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import java.util.List;
@Service
public class UserService {
//自动注入
@Autowired
private UserMapper userMapper;
public User getUser(Integer id) {
return userMapper.selectByPrimaryKey(id);
}
public int insertUser(User user) throws Exception {
//插入不会滚的id=5
User noRollUser = new User();
noRollUser.setId(5);
noRollUser.setName("测试5");
userMapper.insert(noRollUser);
//设置回滚点
Object savePoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint();
try{
int count = userMapper.insert(user);
if( count > 0){
throw new RuntimeException("测试uncheck异常导致回滚");
// throw new Exception("测试check异常不会导致回滚");
}
return count;
}catch(Exception e){
//捕获异常后,回滚到savePoint
TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint);
throw e;
}
}
@Transactional
public int insertUsers(List<User> userList) throws Exception {
int count = 0;
for(User user : userList){
//调用子方法insertUser,将使用注解@Transactional定义的传播行为
//insertUser方法没有定义传播行为,它会采用REQUIRED(默认)
//也就是沿用当前的事务,所以它将与insertUsers方法使用同一个事务
count += insertUser(user);
}
return count;
}
}
结果:只回滚了4,没回滚5
3.5解决方法2:@Transactional的rollbackFor
直接抛出异常,这里不能捕获异常,否则会让rollbackFor失效
Service
package com.demo.services;
import com.demo.models.mapper.AppadminMapper;
import com.demo.models.mapper.UserMapper;
import com.demo.models.po.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import java.util.List;
@Service
public class UserService {
//自动注入
@Autowired
private UserMapper userMapper;
public User getUser(Integer id) {
return userMapper.selectByPrimaryKey(id);
}
public int insertUser(User user) throws Exception {
int count = userMapper.insert(user);
if( count > 0){
throw new Exception("测试check异常不会导致回滚");
}
return count;
}
@Transactional(
//隔离级别:
//一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读
isolation = Isolation.READ_COMMITTED,
//传播行为:
// REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
propagation = Propagation.REQUIRED,
//指定异常类
rollbackFor = Exception.class //{RuntimeException.class, Exception.class}
)
public int insertUsers(List<User> userList) throws Exception {
int count = 0;
for(User user : userList){
//调用子方法insertUser,将使用注解@Transactional定义的传播行为
//1.insertUser方法没有定义传播行为,它会采用REQUIRED,
//也就是沿用当前的事务,所以它将与insertUsers方法使用同一个事务
count += insertUser(user);
}
return count;
}
}
结果:会回滚
3.6 解决方法3:捕获异常,手动回滚
3.6.1 注意这里要在@Transactional的类/方法及其子方法中捕获并处理,在之外无效
3.6.2 捕获异常不再抛出时,rollbackFor指定了也不会回滚
注意这里无论是checked还是unchecked异常,只要捕获了不再抛出,都不会回滚
Service
package com.demo.services;
import com.demo.models.mapper.AppadminMapper;
import com.demo.models.mapper.UserMapper;
import com.demo.models.po.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import java.util.List;
@Service
public class UserService {
//自动注入
@Autowired
private UserMapper userMapper;
public User getUser(Integer id) {
return userMapper.selectByPrimaryKey(id);
}
public int insertUser(User user) throws Exception {
try{
int count = userMapper.insert(user);
if( count > 0){
//throw new RuntimeException("测试uncheck异常导致回滚");
throw new Exception("测试check异常不会导致回滚");
}
return count;
}catch(Exception e){
//要处理回滚,不然即使是unchecked也无效
return 0;
}
}
@Transactional(
//隔离级别:
//一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读
isolation = Isolation.READ_COMMITTED,
//传播行为:
// REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
propagation = Propagation.REQUIRED,
//指定异常类
rollbackFor = Exception.class
)
public int insertUsers(List<User> userList) throws Exception {
int count = 0;
for(User user : userList){
//调用子方法insertUser,将使用注解@Transactional定义的传播行为
//1.insertUser方法没有定义传播行为,它会采用REQUIRED,
//也就是沿用当前的事务,所以它将与insertUsers方法使用同一个事务
count += insertUser(user);
}
return count;
}
}
结果:不会回滚
3.6.3 正确用法:在捕获异常后手动回滚
package com.demo.services;
import com.demo.models.mapper.AppadminMapper;
import com.demo.models.mapper.UserMapper;
import com.demo.models.po.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import java.util.List;
@Service
public class UserService {
//自动注入
@Autowired
private UserMapper userMapper;
public User getUser(Integer id) {
return userMapper.selectByPrimaryKey(id);
}
public int insertUser(User user) throws Exception {
try{
int count = userMapper.insert(user);
if( count > 0){
//throw new RuntimeException("测试uncheck异常导致回滚");
throw new Exception("测试check异常不会导致回滚");
}
return count;
}catch(Exception e){
//结合@Transactional(rollbackFor = Exception.class)使用,才会回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return 0;
}
}
@Transactional(
//隔离级别:
//一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读
isolation = Isolation.READ_COMMITTED,
//传播行为:
// REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
propagation = Propagation.REQUIRED,
//指定异常类
rollbackFor = Exception.class //{RuntimeException.class, Exception.class}
)
public int insertUsers(List<User> userList) throws Exception {
int count = 0;
for(User user : userList){
//调用子方法insertUser,将使用注解@Transactional定义的传播行为
//1.insertUser方法没有定义传播行为,它会采用REQUIRED,
//也就是沿用当前的事务,所以它将与insertUsers方法使用同一个事务
count += insertUser(user);
}
return count;
}
}
结果:会回滚
4.事务属性设置
@Transactional(
//隔离级别:
//一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读
isolation = Isolation.READ_COMMITTED,
//传播行为:
// REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
propagation = Propagation.REQUIRED
// SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
// propagation = Propagation.SUPPORTS
)
public int insertUsers(List<User> userList) throws Exception {
int count = 0;
for(User user : userList){
//调用子方法insertUser,将使用注解@Transactional定义的传播行为
//1.insertUser方法没有定义传播行为,它会采用REQUIRED,
//也就是沿用当前的事务,所以它将与insertUsers方法使用同一个事务
//2.SUPPORTS:insertUser方法没有定义传播行为,跟着采用SUPPORTS
count += insertUser(user);
}
return count;
}
5.总结
事务的回滚,一定在@Transactional的类/方法及其子方法中,在外面是无效的,所以一般异常捕获可以放在外面
为了同时回滚unchecked和checked异常,在@Transactional范围内,可以采用:
1.捕获异常+设置回滚点
2.捕获异常+rollbackFor+手动回滚setRollbackOnly