一、测试前的准备
数据表结构:
1. StuService
package com.ybqdren.service;
import com.ybqdren.pojo.Stu;
/**
* Wen(Joan) Zhao <withzhaowen@126.com>
* 2021/9/15
*/
public interface StuService {
public Stu getStuInfo(int id);
public void saveStu();
public void saveStu(int id);
public void updateStu(int id);
public void deleteStu(int id);
public void saveParent();
public void saveChildren();
}
2. StuServiceImpl
package com.ybqdren.service.impl;
import com.ybqdren.mapper.StuMapper;
import com.ybqdren.pojo.Stu;
import com.ybqdren.service.StuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* Wen(Joan) Zhao <withzhaowen@126.com>
* 2021/9/15
*/
@Service
public class StuServiceImpl implements StuService {
@Autowired
private StuMapper stuMapper;
//@Transactional(propagation = Propagation.SUPPORTS)
@Override
public Stu getStuInfo(int id) {
return (Stu) stuMapper.selectByPrimaryKey(id);
}
//@Transactional(propagation = Propagation.REQUIRED)
@Override
public void saveStu() {
Stu stu = new Stu();
stu.setName("jack");
stu.setAge(18);
stuMapper.insert(stu);
}
// @Transactional(propagation = Propagation.REQUIRED)
@Override
public void saveStu(int id) {
}
//@Transactional(propagation = Propagation.REQUIRED)
@Override
public void updateStu(int id) {
Stu stu = new Stu();
stu.setId(id);
stu.setName("lucy");
stu.setAge(20);
stuMapper.updateByPrimaryKey(stu);
}
//@Transactional(propagation = Propagation.REQUIRED)
@Override
public void deleteStu(int id) {
stuMapper.deleteByPrimaryKey(id);
}
@Override
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(19);
stuMapper.insert(stu);
}
@Override
public void saveChildren() {
saveChild1();
}
public void saveChild1(){
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2(){
Stu stu1 = new Stu();
stu1.setName("child-2");
stu1.setAge(11);
stuMapper.insert(stu1);
}
}
3. TestTransService
package com.ybqdren.service;
public interface TestTransService {
public void testPropagationTrans();
}
4. TestTransServiceImpl
package com.ybqdren.service.impl;
import com.ybqdren.service.StuService;
import com.ybqdren.service.TestTransService;
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 TestTransServiceImpl implements TestTransService {
@Autowired
private StuService stuService;
// @Transactional(propagation = Propagation.REQUIRED)
@Override
public void testPropagationTrans() {
stuService.saveParent();
}
}
5. TransTest
import com.ybqdren.Application;
import com.ybqdren.service.StuService;
import com.ybqdren.service.TestTransService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* Wen(Joan) Zhao <withzhaowen@126.com>
* 2021/9/19
*/
@SpringBootTest(classes = Application.class)
public class TransTest {
@Autowired
private StuService stuService;
@Autowired
private TestTransService testTransService;
@Test
public void myTest() {
testTransService.testPropagationTrans();
}
}
二、不使用事务
1. StuServiceImpl
package com.ybqdren.service.impl;
import com.ybqdren.mapper.StuMapper;
import com.ybqdren.pojo.Stu;
import com.ybqdren.service.StuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* Wen(Joan) Zhao <withzhaowen@126.com>
* 2021/9/15
*/
@Service
public class StuServiceImpl implements StuService {
@Autowired
private StuMapper stuMapper;
// .....
@Override
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(19);
stuMapper.insert(stu);
}
@Override
public void saveChildren() {
saveChild1();
// 在两次添加数据的操作之间加入一个绝对会报异常的代码
int a = 1/0;
saveChild2();
}
public void saveChild1(){
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2(){
Stu stu1 = new Stu();
stu1.setName("child-2");
stu1.setAge(11);
stuMapper.insert(stu1);
}
}
2. TestTransServiceImpl
package com.ybqdren.service.impl;
import com.ybqdren.service.StuService;
import com.ybqdren.service.TestTransService;
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 TestTransServiceImpl implements TestTransService {
@Autowired
private StuService stuService;
// @Transactional(propagation = Propagation.REQUIRED)
@Override
public void testPropagationTrans() {
stuService.saveParent();
// 新增
stuService.saveChildren();
}
}
3. 操作执行后的数据库
1) 报异常
2)数据库(前后)
可以看到saveChildren只保存了一数据,child-2并没有插入进去
在发生异常后,child-2是无法保存到数据库的,也没有进行回滚
三、事务
1. REQUIRED
1)简介
- 使用当前的事务,如果当前没有事务,则自己新建一个事务,子方法是必须运行在一个事务中的;
- 如果当前存在事务,则加入这个事务,成为一个整体。
2)在父方法中添加
在TestTransServiceImpl中开启事务
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
}
执行后
可以看到,此处数据库中并没有插入数据
因为在此处进行了事务传播REQUIRED,虽然saveParent和saveChild方法都没有事务,但是它们也传播到了REQUIRED事务
所以当父方法使用REQUIRED事务时,子方法发生异常后,两个子方法中的事务都会进行回滚
3)在单个子方法中添加
清除父方法上的事务
// @Transactional(propagation = Propagation.REQUIRED) @Override public void testPropagationTrans() { stuService.saveParent(); stuService.saveChildren(); }
在saveChild方法上添加一个事务
@Override public void saveParent() { Stu stu = new Stu(); stu.setName("parent"); stu.setAge(19); stuMapper.insert(stu); } @Transactional(propagation = Propagation.REQUIRED) @Override public void saveChildren() { saveChild1(); int a = 1/0; saveChild2(); }public void saveChild1(){ Stu stu1 = new Stu(); stu1.setName("child-1"); stu1.setAge(11); stuMapper.insert(stu1);}public void saveChild2(){ Stu stu1 = new Stu(); stu1.setName("child-2"); stu1.setAge(11); stuMapper.insert(stu1);}
执行后
可以看到saveParent方法执行的数据添加到了数据库,而saveChild没有。
也就是说,当前的整个方法方法体中不带有事务时,他回去重新创建一个事务
也就是说saveChildren方法必须要存在于某个事务中去运行,所以它发生异常后进行了回滚,而saveParent没有事务。
2. SUPPORTS
1)简介
如果当前有事务,则使用事务;如果当前没有事务,则不使用事务。
2)外层没有事务,而子方法有
@Overridepublic void testPropagationTrans() { stuService.saveParent(); stuService.saveChildren();}
@Overridepublic void saveParent() { Stu stu = new Stu(); stu.setName("parent"); stu.setAge(19); stuMapper.insert(stu);}@Transactional(propagation = Propagation.SUPPORTS)@Overridepublic void saveChildren() { saveChild1(); int a = 1/0; saveChild2();}public void saveChild1(){ Stu stu1 = new Stu(); stu1.setName("child-1"); stu1.setAge(11); stuMapper.insert(stu1);}public void saveChild2(){ Stu stu1 = new Stu(); stu1.setName("child-2"); stu1.setAge(11); stuMapper.insert(stu1);}
执行后
3)外层使用事务,而内层其中一个子方法也是用事务
@Transactional(propagation = Propagation.REQUIRED)@Overridepublic void testPropagationTrans() { stuService.saveParent(); stuService.saveChildren();}
@Overridepublic void saveParent() { Stu stu = new Stu(); stu.setName("parent"); stu.setAge(19); stuMapper.insert(stu);}@Transactional(propagation = Propagation.SUPPORTS)@Overridepublic void saveChildren() { saveChild1(); int a = 1/0; saveChild2();}public void saveChild1(){ Stu stu1 = new Stu(); stu1.setName("child-1"); stu1.setAge(11); stuMapper.insert(stu1);}public void saveChild2(){ Stu stu1 = new Stu(); stu1.setName("child-2"); stu1.setAge(11); stuMapper.insert(stu1);}
执行后
前后都为空
3. MANDATORY
1)简介
该传播属性强制必须存在一个事务,如果不存在,则抛出异常。
2)外层无事务,其中一个子方法有事务,而另一个方法没有事务
@Override public void testPropagationTrans() { stuService.saveParent(); stuService.saveChildren(); }
@Overridepublic void saveParent() { Stu stu = new Stu(); stu.setName("parent"); stu.setAge(19); stuMapper.insert(stu);}@Transactional(propagation = Propagation.MANDATORY)@Overridepublic void saveChildren() { saveChild1(); int a = 1/0; saveChild2();}public void saveChild1(){ Stu stu1 = new Stu(); stu1.setName("child-1"); stu1.setAge(11); stuMapper.insert(stu1);}public void saveChild2(){ Stu stu1 = new Stu(); stu1.setName("child-2"); stu1.setAge(11); stuMapper.insert(stu1);}
执行后
控制台报错
数据库
3)外层有事务,子方法一个方法有事务,另一个没有
@Transactional(propagation = Propagation.REQUIRED) @Override public void testPropagationTrans() { stuService.saveParent(); stuService.saveChildren(); }
@Overridepublic void saveParent() { Stu stu = new Stu(); stu.setName("parent"); stu.setAge(19); stuMapper.insert(stu);}@Transactional(propagation = Propagation.MANDATORY)@Overridepublic void saveChildren() { saveChild1(); int a = 1/0; saveChild2();}public void saveChild1(){ Stu stu1 = new Stu(); stu1.setName("child-1"); stu1.setAge(11); stuMapper.insert(stu1);}public void saveChild2(){ Stu stu1 = new Stu(); stu1.setName("child-2"); stu1.setAge(11); stuMapper.insert(stu1);}
执行后
数据库
4. REQUIRES_NEW
1)简介
- 如果当前有事务,则挂起该事务,并且自己创建一个新的事务给自己使用;
- 如果当前没有事务,则同 REQUIRED。
2)外层无事务,子方法有事务,而父方法没有事务
@Override public void testPropagationTrans() { stuService.saveParent(); stuService.saveChildren(); }
@Overridepublic void saveParent() { Stu stu = new Stu(); stu.setName("parent"); stu.setAge(19); stuMapper.insert(stu);}@Transactional(propagation = Propagation.REQUIRES_NEW)@Overridepublic void saveChildren() { saveChild1(); int a = 1/0; saveChild2();}public void saveChild1(){ Stu stu1 = new Stu(); stu1.setName("child-1"); stu1.setAge(11); stuMapper.insert(stu1);}public void saveChild2(){ Stu stu1 = new Stu(); stu1.setName("child-2"); stu1.setAge(11); stuMapper.insert(stu1);}
执行后
数据库
分析
@Override public void testPropagationTrans() { stuService.saveParent(); stuService.saveChildren(); }
saveParent没有事务,而saveChildren有事务,所以会将后者和前者操作区分开来,后者发生错误会进行回滚,但是前者会正常存储到数据库中。
3)外层有事务,子方法有事务,父方法没有事务
@Transactional(propagation = Propagation.REQUIRED) @Override public void testPropagationTrans() { stuService.saveParent(); stuService.saveChildren(); }
@Overridepublic void saveParent() { Stu stu = new Stu(); stu.setName("parent"); stu.setAge(19); stuMapper.insert(stu);}@Transactional(propagation = Propagation.REQUIRES_NEW)@Overridepublic void saveChildren() { saveChild1(); int a = 1/0; saveChild2();}public void saveChild1(){ Stu stu1 = new Stu(); stu1.setName("child-1"); stu1.setAge(11); stuMapper.insert(stu1);}public void saveChild2(){ Stu stu1 = new Stu(); stu1.setName("child-2"); stu1.setAge(11); stuMapper.insert(stu1);}
执行后
数据库
前后都为空。
分析
@Transactional(propagation = Propagation.REQUIRES_NEW)@Overridepublic void saveChildren() { saveChild1(); int a = 1/0; saveChild2();}
saveChildren方法执行后会产生1个byzero的异常,而这个异常会传递给其调用方法:
@Transactional(propagation = Propagation.REQUIRED)@Overridepublic void testPropagationTrans() { stuService.saveParent(); stuService.saveChildren();}
因为当前方法有事务REQUIRED,变成了一个整体,所以就会影响当前方法中一起被调用的saveParent,所以也导致saveParent也出现了回滚。
4)外层使用事务也产生异常,子方法有事务,父方法没有事务
@Transactional(propagation = Propagation.REQUIRED)@Overridepublic void testPropagationTrans() { stuService.saveParent(); stuService.saveChildren(); int a = 1/0;}
@Overridepublic void saveParent() { Stu stu = new Stu(); stu.setName("parent"); stu.setAge(19); stuMapper.insert(stu);}@Transactional(propagation = Propagation.REQUIRES_NEW)@Overridepublic void saveChildren() { saveChild1(); saveChild2();}public void saveChild1(){ Stu stu1 = new Stu(); stu1.setName("child-1"); stu1.setAge(11); stuMapper.insert(stu1);}public void saveChild2(){ Stu stu1 = new Stu(); stu1.setName("child-2"); stu1.setAge(11); stuMapper.insert(stu1);}
执行后
数据库
多了两条数据:
分析
因为子方法saveChildren中使用了新的事务,所以当前事务是被挂起的,所以当前方法的事务是不会影响到其子方法的:
@Transactional(propagation = Propagation.REQUIRED)@Overridepublic void testPropagationTrans() { stuService.saveParent(); stuService.saveChildren(); int a = 1/0;}
但是saveParent中没有使用事务,所以会被回滚,所以数据库中会保存saveChildren中插入的值:
5)外层有REQUIRED事务,子方法有REQUIRED事务,父方法没有事务
子方法和外层都是用REQUIRED事务,也就是两个方法都使用同一个事务:
@Transactional(propagation = Propagation.REQUIRED)@Overridepublic void testPropagationTrans() { stuService.saveParent(); stuService.saveChildren(); int a = 1/0;}
@Overridepublic void saveParent() { Stu stu = new Stu(); stu.setName("parent"); stu.setAge(19); stuMapper.insert(stu);}@Transactional(propagation = Propagation.REQUIRED)@Overridepublic void saveChildren() { saveChild1(); // int a = 1/0; saveChild2();}public void saveChild1(){ Stu stu1 = new Stu(); stu1.setName("child-1"); stu1.setAge(11); stuMapper.insert(stu1);}public void saveChild2(){ Stu stu1 = new Stu(); stu1.setName("child-2"); stu1.setAge(11); stuMapper.insert(stu1);}
执行后
没有插入值:
分析
两个方法都共用了同一个事务,所以不管在哪个方法中抛出异常,都会进行回滚。
5. NOT_SUPPORTED
1)简介
如果当前有事务,则把事务挂起,自己不使用事务去运行数据库操作。
2)外层无事务,子方法不使用事务,父方法没有事务
@Overridepublic void testPropagationTrans() { stuService.saveParent(); stuService.saveChildren();}
@Overridepublic void saveParent() { Stu stu = new Stu(); stu.setName("parent"); stu.setAge(19); stuMapper.insert(stu);}@Transactional(propagation = Propagation.NOT_SUPPORTED)@Overridepublic void saveChildren() { saveChild1(); int a = 1/0; saveChild2();}public void saveChild1(){ Stu stu1 = new Stu(); stu1.setName("child-1"); stu1.setAge(11); stuMapper.insert(stu1);}public void saveChild2(){ Stu stu1 = new Stu(); stu1.setName("child-2"); stu1.setAge(11); stuMapper.insert(stu1);}
执行后
数据库
虽然控制台有报异常,但是并没有进行回滚:
分析
外层没有事务,且子方法中也不使用事务,因此产生了异常也没有进行回滚。
3)外层有事务,子方法不使用事务,父方法没有事务
@Transactional(propagation = Propagation.REQUIRED)@Overridepublic void testPropagationTrans() { stuService.saveParent(); stuService.saveChildren();}
@Overridepublic void saveParent() { Stu stu = new Stu(); stu.setName("parent"); stu.setAge(19); stuMapper.insert(stu);}@Transactional(propagation = Propagation.NOT_SUPPORTED)@Overridepublic void saveChildren() { saveChild1(); int a = 1/0; saveChild2();}public void saveChild1(){ Stu stu1 = new Stu(); stu1.setName("child-1"); stu1.setAge(11); stuMapper.insert(stu1);}public void saveChild2(){ Stu stu1 = new Stu(); stu1.setName("child-2"); stu1.setAge(11); stuMapper.insert(stu1);}
执行后
数据库
分析
saveChildren方法中抛出了一个异常,所以只执行到了saveChild1():
@Transactional(propagation = Propagation.NOT_SUPPORTED)@Overridepublic void saveChildren() { saveChild1(); int a = 1/0; saveChild2();}
而因为当前方法是不使用异常的,所以saveChild1是没有发生回滚的。
而调用方是本身有事务的:
@Transactional(propagation = Propagation.REQUIRED)@Overridepublic void testPropagationTrans() { stuService.saveParent(); stuService.saveChildren();}
所以从saveChildren中产生的异常,也会让此处同时被调用的saveParent方法被回滚。
6. NEVER
1)简介
如果当前有事务存在,则抛出异常。
2)外层有事务REQUIRED,而子方法有NEVER事务
@Transactional(propagation = Propagation.REQUIRED)@Overridepublic void testPropagationTrans() { stuService.saveParent(); stuService.saveChildren();}
@Overridepublic void saveParent() { Stu stu = new Stu(); stu.setName("parent"); stu.setAge(19); stuMapper.insert(stu);}@Transactional(propagation = Propagation.NEVER)@Overridepublic void saveChildren() { saveChild1(); int a = 1/0; saveChild2();}public void saveChild1(){ Stu stu1 = new Stu(); stu1.setName("child-1"); stu1.setAge(11); stuMapper.insert(stu1);}public void saveChild2(){ Stu stu1 = new Stu(); stu1.setName("child-2"); stu1.setAge(11); stuMapper.insert(stu1);}
执行后
提示已经有事务存在的异常:
数据库
没有任何数据插入:
3)外层没有事务,子方法有NEVER事务
@Override public void testPropagationTrans() { stuService.saveParent(); stuService.saveChildren(); }
@Overridepublic void saveParent() { Stu stu = new Stu(); stu.setName("parent"); stu.setAge(19); stuMapper.insert(stu);}@Transactional(propagation = Propagation.NEVER)@Overridepublic void saveChildren() { saveChild1(); int a = 1/0; saveChild2();}public void saveChild1(){ Stu stu1 = new Stu(); stu1.setName("child-1"); stu1.setAge(11); stuMapper.insert(stu1);}public void saveChild2(){ Stu stu1 = new Stu(); stu1.setName("child-2"); stu1.setAge(11); stuMapper.insert(stu1);}
执行后
数据库
分析
没有事务,就没有相应的回滚。
7. NESTED
1)简介
- 如果当前有事务,则开启子事务(嵌套事务),嵌套事务是独立提交或者回滚;如果当前没有事务,则同 REQUIRED。
- 但是如果主事务提交,则会携带子事务一起提交。
- 如果主事务回滚,则子事务会一起回滚。相反,子事务异常,则父事务可以回滚或不回滚。
2)外层使用REQUIRED事务有异常,子方法使用NESTED事务无异常
@Transactional(propagation = Propagation.REQUIRED)@Overridepublic void testPropagationTrans() { stuService.saveParent(); stuService.saveChildren(); int a = 1/0;}
@Overridepublic void saveParent() { Stu stu = new Stu(); stu.setName("parent"); stu.setAge(19); stuMapper.insert(stu);}@Transactional(propagation = Propagation.NESTED)@Overridepublic void saveChildren() { saveChild1(); saveChild2();}public void saveChild1(){ Stu stu1 = new Stu(); stu1.setName("child-1"); stu1.setAge(11); stuMapper.insert(stu1);}public void saveChild2(){ Stu stu1 = new Stu(); stu1.setName("child-2"); stu1.setAge(11); stuMapper.insert(stu1);}
执行后
数据库
什么都没有:
分析
有父子嵌套事务存在话,在调用方法中发生的异常,那么在嵌套事务里面的事务也会被回滚。
3)外层使用REQUIRED事务无异常,子方法使用NESTED事务有异常
@Transactional(propagation = Propagation.REQUIRED)@Overridepublic void testPropagationTrans() { stuService.saveParent(); try{ // save point 保存点: 对当前事务的异常进行catch之后,就不会影响同层次调用的其他方法 其他操作 stuService.saveChildren(); }catch (Exception e){ e.printStackTrace(); }}
@Overridepublic void saveParent() { Stu stu = new Stu(); stu.setName("parent"); stu.setAge(19); stuMapper.insert(stu);}@Transactional(propagation = Propagation.NESTED)@Overridepublic void saveChildren() { saveChild1(); int a = 1/0; saveChild2();}public void saveChild1(){ Stu stu1 = new Stu(); stu1.setName("child-1"); stu1.setAge(11); stuMapper.insert(stu1);}public void saveChild2(){ Stu stu1 = new Stu(); stu1.setName("child-2"); stu1.setAge(11); stuMapper.insert(stu1);}
执行后
数据库
分析
使用save point 将saveChildren方法中的异常隔离开,不会影响到当前调用方中其他的方法。