事务@Transactional
前言:相关jar
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
根据@Transactional(propagation= Propagation.NOT_SUPPORTED)讲解
一.源码
package org.springframework.transaction.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
@AliasFor("transactionManager")
String value() default "";
@AliasFor("value")
String transactionManager() default "";
//默认
Propagation propagation() default Propagation.REQUIRED;
Isolation isolation() default Isolation.DEFAULT;
int timeout() default -1;
boolean readOnly() default false;
Class<? extends Throwable>[] rollbackFor() default {};
String[] rollbackForClassName() default {};
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
}
可以看到什么都不写的时候默认Propagation.REQUIRED,是指当前的事务一定要使用一个事务的,就是当前的事务不存在,则会创建一个新的事务,如果存在事务,则会加入到当前事务中
二.点进去Propagation
package org.springframework.transaction.annotation;
public enum Propagation {
REQUIRED(0), //使用当前事务,如果当前没有事务,则自己新建一个事务,子方法是必须运行在一个事务中的,如果当前存在事务,则加入这个事务,成为一个整体
SUPPORTS(1),//如果当前有事务,则使用事务,如果当前没有事务,则不使用事务
MANDATORY(2),//该属性强制必须存在一个事务,如果当前没有事务,就抛出异常
REQUIRES_NEW(3),//如果当前有事务,则挂起该事务,并且自己创建一个新的事务给自己使用;如果没有事务,同REQUIRED
NOT_SUPPORTED(4),//以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
NEVER(5),//如果当前存在事务,则抛出异常
NESTED(6);/*如果当前有事务,则开启子事务(嵌套事务),嵌套事务是独立提交或者 回滚;如果当前没有事务,则同REQUIRED,但是如果主事务提交,则会携带子事务一* 起提交。
* 如果主事务回滚,则子事务一起回滚,相反,子事务异常,则父事务可以混滚或者不回滚。*/
private final int value;
private Propagation(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}
ctrl+f12可以调出当前类的所有方法以列表的方式展示出来
三.事务不生效的的几种方法
1.本类方法调用,假如有一个类Test,有一个方法A,方法A调用方法B(不论方法B是用public还是private修饰,此时方法B声明事务,但是方法A没声明事务,外部方法调用方法A时,方法B的事务是不会起作用的
如:
//11 在service写下如下方法
@Override
// @Transactional(propagation = Propagation.REQUIRED)
public void testPropagation() {
saveParent();
saveChildren();
}
//22
public void saveParent() {
User user = new User();
user.setName("张三");
user.setAge(00);
user.setEmail("123456@qq.com");
userMapper.insert(user);
}
//33
// @Transactional(propagation = Propagation.REQUIRED)
public void saveChildren(){
saveChild1();
//此处留一处异常便于测试
int a = 1/0;
saveChild2();
}
//44
public void saveChild1(){
User user = new User();
user.setName("child-1");
user.setAge(11);
user.setEmail("123456@qq.com");
userMapper.insert(user);
}
//55
public void saveChild2(){
User user = new User();
user.setName("child-2");
user.setAge(22);
user.setEmail("654321@qq.com");
userMapper.insert(user);
}
//然后test里面新建一个test
package com.test;
import com.river.Application;
import com.river.UserService;
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.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class BasisTest {
@Autowired
private UserService userService;
@Test
public void testPro(){
userService.testPropagation();
System.err.println("运行成功");
}
}
①:把所有的事务都注释掉,放开33的事务 里面有这么一段话@Transactional(propagation = Propagation.REQUIRED)
可以看到本类方法11调用本类方法22和33,在22或者33上加上的事务是不会起作用的
2.异常被你的 catch“吃了”导致@Transactional失效
3.数据库引擎不支持事务,例如myisam
四.事务使用的七种方式
下面贴出三段代码
UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
//11
@Override
public void saveParent() {
User user = new User();
user.setName("张三");
user.setAge(0);
user.setEmail("123456@qq.com");
userMapper.insert(user);
}
//22
@Override
//@Transactional(propagation = Propagation.REQUIRED)
public void saveChildren(){
saveChild1();
int a = 1/0;//此处异常,因为0不能当除数
saveChild2();
}
//33
public void saveChild1(){
User user = new User();
user.setName("child-1");
user.setAge(11);
user.setEmail("123456@qq.com");
userMapper.insert(user);
}
//44
public void saveChild2(){
User user = new User();
user.setName("child-2");
user.setAge(22);
user.setEmail("654321@qq.com");
userMapper.insert(user);
}
}
TruckServiceImpl
@Service
public class TruckServiceImpl implements TruckService {
@Autowired
private UserService userService;
//55
@Override
//@Transactional(propagation = Propagation.REQUIRED)
public void testPro() {
userService.saveParent();
userService.saveChildren();
//int a = 1/0;//此处异常,因为0不能当除数
}
}
BasisTest
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class BasisTest {
@Autowired
private TruckService truckService;
@Test
public void testPro(){
truckService.testPro();
System.err.println("运行成功");
}
}
1.事务的传播:Propagation.REQUIRED(多用于增,删,改,什么都不写的时候是默认是它)
①:把所有的事务都注释掉,运行BasisTest
结果:
可以看到:22的方法中有一处明显的异常,所以是执行不下去的,在异常之前的数据都已经执行,异常如下
结论:当方法中没有事务的时候,异常之前的方法会执行,异常之后的方法不执行
②:把父类方法55的@Transactional(propagation = Propagation.REQUIRED)注释放开
可以看到,什么数据也没有
结论:事务进行了传播,父方法的事务会传递到子方法中,当子方法中遇到异常,事务都会回滚
③:父方法事务注释,把子方法中的带注释为22的事务@Transactional(propagation = Propagation.REQUIRED)放开
父级方法中没有事务,而子级方法中有事务,可以看到
不带有事务方法的数据进行了插入,而带有事务的方法遇到了异常进行了回滚
结论:如果一个方法带有事务,内部调用的方法出现了异常,它和它的子方法都会进行回滚;这个方法之前的方法不带有事务的话,正常运行,不会回滚
④:把22和55的@Transactional(propagation = Propagation.REQUIRED)都放开
可以看到,什么数据都不会有
父方法和子方法都有事务,并且数据也进行了回滚
结论:如果当前存在事务,则加入这个事务,成为一个整体
2.事务的的支持Propagation.SUPPORTS(多用于查询)
①:注释掉所有事务,把22的事务换成@Transactional(propagation = Propagation.SUPPORTS)
可以看到,异常之前的方法都已经执行
结论:如果父方法中没有事务,就以没有事务的方式运行
②:父方法中加上@Transactional(propagation = Propagation.REQUIRED),子方法加上@Transactional(propagation = Propagation.SUPPORTS)
可以看到
结论:如果当前存在事务,就以事务的方式运行,如果不存在事务,就以非事务的方式运行
3.事务的强制Propagation.MANDATORY
类似于EJB的事务属性
①:注释掉所有事务,在子方法注释为22中增加事务@Transactional(propagation = Propagation.MANDATORY)
可以看到如下异常
结论:使用Propagation.MANDATORY时,父方法必须有事务,如果没有事务,则会抛出异常
②:父方法55加上事务@Transactional(propagation = Propagation.REQUIRED),子方法22加上事务@Transactional(propagation = Propagation.MANDATORY)
可以看到,数据进行了回滚
结论:MANDATORY必须有父事务,没有的话抛出异常
4.事务的挂起@Transactional(propagation = Propagation.REQUIRES_NEW)
①:注释掉所有事务,在22的子方法中加上事务@Transactional(propagation = Propagation.REQUIRES_NEW)
可以看到:父方法中没有加事务的的方法运行了,子方法加事务的,有异常就回滚了
结论:REQUIRES_NEW会创建一个新事务
②:放开55的事务注释@Transactional(propagation = Propagation.REQUIRED),和22事务共用
父方法一个事务,子方法新创建一个事务,当子方法遇到异常时,会回滚,回滚以后本身会有一个异常,所以父方法也会回滚,就会影响到父方法,导致没有数据
结论:当父方法有事务时,子方法REQUIRES_NEW会新创建一个事务,当子方法的事务遇到异常时,父方法的也会进行回滚
③:把22的异常注释掉,加入到父方法的末尾中,如55的注释位置
可以看到,父方法中的没加REQUIRES_NEW事务的进行了回滚,而加REQUIRES_NEW则是创建了一个新事务进行了数据操作
结论:如果存在事务,则创建一个新的事务单独执行,如果不存在,创建一个新事务
5.事务的挂起Propagation.NOT_SUPPORTED
把55的主方法的异常删除掉,放开子方法22的异常
①:在子方法中注释为22的上面加上注解@Transactional(propagation = Propagation.NOT_SUPPORTED)
可以看到
结论:在子方法中调用并没有出现事务
②:在父方法55中加上@Transactional(propagation = Propagation.REQUIRED),子方法中22中加入@Transactional(propagation = Propagation.NOT_SUPPORTED)
可以看到,主方法的数据没有保存,子方法的异常之前的都保存了
子方法抛异常没有回滚,主方法却回滚了
结论:如果当前有事务,则把事务挂起,自己不使用事务进行操作
6.事务的传播@Transactional(propagation = Propagation.NEVER)
①:主方法事务注释放开,子方法加上 Propagation.NEVER,会报错
有事务则会报异常
注释掉主方法的事务
可以看到,异常之前的方法都插入了数据库
结论:子方法如果加入@Transactional(propagation = Propagation.NEVER),则主方法不允许加事务
7.事务的传播@Transactional(propagation = Propagation.NESTED)
①:子方法22加上事务@Transactional(propagation = Propagation.NESTED),注释掉异常,主方法55放开注释,加上事务,并且在末尾加上异常
可以看到,数据库中没有数据,事务被回滚
结论:当子方法加上@Transactional(propagation = Propagation.NESTED)时,如果主方法有异常,则会回滚
②:主方法55事务注释放开,子方法加入事务@Transactional(propagation = Propagation.NESTED)子方法中加入try …catch,主方法异常注释,子方法异常放开
可以看到,异常由于被捕捉,子方法异常之前的方法被执行
结论:当子方法的异常被try … catch时,事务会在异常之前运行,且不会回滚
③:注释掉主方法的事务,只留子方法的事务@Transactional(propagation = Propagation.NESTED)
②:如果主方法没有事务,则同于@Transactional(propagation = Propagation.REQUIRED)
可以看到
作用同REQUIRED一样
结论:当主方法没有事务的时候,子方法加入注解@Transactional(propagation = Propagation.REQUIRED),主方法的语句插入数据库,子方法中有异常则回滚。