一、概述
在平常业务的编写中,事务是一个比较频繁使用的东西。Spring为我们提供了一种基于AOP实现的注解类型@Transtation,方便我们对业务进行事务的使用。
二、@Transactional组成
readOnly:设置当前是否是只读事务,默认false;
rollbackFor:需要进行回滚的异常类,事务遇到这种异常就回滚;
propagation:事务传播类型,默认Propagation.REQUIRED;
isolation:事务隔离级别;
比较基础和常见的这些就差不多。
三、事务注解的使用
准备
@Data
@AllArgsConstructor
public class Studen implements Serializable {
private Integer id;
private String name;
private Integer age;
}
@Service
public class StudenServiceImpl{
@Autowired
private StudenMapper studenMapper;
public void insert(Studen studen){
studenMapper.insert(studen);
}
public void delete(int id){
studenMapper.delete(id);
}
public void update(Studen studen){
studenMapper.update(studen);
}
public List<Studen> selectList(){
return studenMapper.selectList();
}
@Transactional
public void updateAndInsert(Studen studen,Studen newStudenb){
studenMapper.update(studen);
//制造异常
int a = 1/0;
studenMapper.insert(newStudenb);
}
@Transactional
void updateAndInsert2(Studen studen,Studen newStudenb){
studenMapper.update(studen);
//制造异常
int a = 1/0;
studenMapper.insert(newStudenb);
}
//改进前
//@Transactional
//改进后
@Transactional(rollbackFor = Exception.class)
public void updateAndInsert3(Studen studen,Studen newStudenb) throws FileNotFoundException {
studenMapper.update(studen);
//制造非运行时异常 FileNotFoundException
FileInputStream fileInputStream = new FileInputStream(new File("A:QWEQW"));
studenMapper.insert(newStudenb);
}
@Transactional
public void updateAndInsert4(Studen studen,Studen newStudenb){
studenMapper.update(studen);
//制造运行时异常并手动try catch
try{
int a = 1/0;
}catch (ArithmeticException e){
e.printStackTrace();
}
studenMapper.insert(newStudenb);
}
public void interDo(Studen s,Studen t){
this.updateAndInsert(s,t);
}
}
//初始化数据
@Test
public void initData(){
Studen san = new Studen(1,"张三",18);
Studen si = new Studen(2,"李四",23);
Studen wu = new Studen(3,"王五",21);
studenService.insert(san);
studenService.insert(si);
studenService.insert(wu);
List<Studen> studens = studenService.selectList();
System.out.println(studens);
}
1. 正常使用案例
代码案例
//测试studenService.updateAndInsert,该方法添加事务所以正常回滚
@Test
public void test1(){
Studen uStuden = new Studen(1, "zhangsan", 10);
Studen nStuden = new Studen(4, "huhu", 28);
studenService.updateAndInsert(uStuden,nStuden);
}
2.注解失效案例
失效情况
- 修饰的方法不是public;
- 代码中发生的异常不属于RuntimeException / error;
- 代码中try catch处理异常
- 同类方法调用
失败案例测试
场景一:修饰符非public
//studenService.updateAndInsert2使用默认修饰符,
所以利用studen2Service类去调用,该事务并没有起作用
@Test
public void test2(){
Studen uStuden = new Studen(1, "zhangsan2", 10);
Studen nStuden = new Studen(4, "huhu2", 28);
studen2Service.testM(uStuden,nStuden);
}
场景二:异常不属于RuntimeException
//此处调用的方法其中会抛出一个FileNotFoundException,这个异常
//不属于RuntimeException所以事务并没有回滚。
//将事务注解的属性rollbackFor设置更大的范围即可解决
@Test
public void test3() throws FileNotFoundException {
Studen uStuden = new Studen(1, "zhangsan3", 10);
Studen nStuden = new Studen(4, "huhu3", 28);
studenService.updateAndInsert3(uStuden,nStuden);
}
改进方法就是在rollbackFor属性中规定捕获的异常范围
@Transactional(rollbackFor = Exception.class)
改进后结果再次执行该方法,发现事务生效
场景三:try catch捕获处理
//此处由于手动处理异常事务并没有察觉到有异常所以没有回滚
@Test
public void test4() throws FileNotFoundException {
Studen uStuden = new Studen(1, "zhangsan4", 14);
Studen nStuden = new Studen(4, "huhu4", 24);
studenService.updateAndInsert4(uStuden,nStuden);
}
场景四:内部方法调用
//此处是调用的interDo方法,该方法没有事务注解,但是调用的updateAndInsert却是
//有事务的,但是interDo实在类里调用的updateAndInsert并不会触发到updateAndInsert
//方法的AOP也就是不会触发到事务注解所以此处事务没有回滚
@Test
public void test5(){
Studen uStuden = new Studen(1, "zhangsan5", 15);
Studen nStuden = new Studen(5, "zhuzhu5", 34);
studenService.interDo(uStuden,nStuden);
}
这个地方重点在于理解清楚AOP的原理,因为@Transactional其实就是基于AOP实现的。