1.使用场景
1,场景一,最常见的用法,在方法上使用@Transactional 注解,事务正常起作用。无异常时正常提交,有异常时数据回滚,代码如下。
@Service
public class ComeServiceImpl implements ComeService {
@Autowired
UserMapper userMapper;
@Override
@Transactional
public int saveUser() {
User user1 = new User(11,"a",111,"a");
userMapper.saveUser(user1);
// int i = 1 / 0;
User user2 = new User(11,"b",111,"b");
userMapper.saveUser(user2);
return 0;
}
}
2,场景二,常见的用法,在类上使用@Transactional 注解,对整个类的方法,事务起作用。无异常时正常提交,有异常时数据回滚,代码如下。
@Service
@Transactional
public class ComeServiceImpl implements ComeService {
@Autowired
UserMapper userMapper;
@Override
public int saveUser() {
User user1 = new User(11,"a",111,"a");
userMapper.saveUser(user1);
User user2 = new User(11,"b",111,"b");
userMapper.saveUser(user2);
// int i = 1 / 0;
return 0;
}
}
3,场景三,将异常信息使用try-catch 包裹,异常被处理,@Transactional 注解不起作用,数据提交,没有回滚,代码如下。
@Service
@Slf4j
public class ComeServiceImpl implements ComeService {
@Autowired
UserMapper userMapper;
@Override
@Transactional
public int saveUser() {
User user1 = new User(11,"a",111,"a");
userMapper.saveUser(user1);
User user2 = new User(11,"b",111,"b");
userMapper.saveUser(user2);
try {
int i = 1 / 0;
}catch (Exception e){
System.out.println("异常。。。");
}
return 0;
}
}
4,场景四,同一个Service内方法调用,当@Transactional 注解作用在A方法上时,事务起作用。方法A中的数据回滚,方法B中的数据回滚,代码如下。
@Service
@Slf4j
public class ComeServiceImpl implements ComeService {
@Autowired
UserMapper userMapper;
@Override
@Transactional
public int A() {
User user1 = new User(11,"a",111,"a");
userMapper.saveUser(user1);
this.B();
return 0;
}
@Override
public int B() {
User user2 = new User(11,"b",111,"b");
userMapper.saveUser(user2);
int i = 1 / 0;
return 0;
}
}
5,场景五,同一个Service内方法调用,当@Transactional 注解作用在B方法上时,事务不起作用。方法A中的数据提交,方法B中数据提交,遇到异常没有回滚,代码如下。
@Service
@Slf4j
public class ComeServiceImpl implements ComeService {
@Autowired
UserMapper userMapper;
@Override
public int A() {
User user1 = new User(11,"a",111,"a");
userMapper.saveUser(user1);
this.B();
return 0;
}
@Override
@Transactional
public int B() {
User user2 = new User(11,"b",111,"b");
userMapper.saveUser(user2);
int i = 1 / 0;
return 0;
}
}
6,场景六,同一个Service内方法调用,当@Transactional 注解作用在类上时,事务起作用,数据回滚,代码如下。
@Service
@Slf4j
@Transactional
public class ComeServiceImpl implements ComeService {
@Autowired
UserMapper userMapper;
@Override
public int A() {
User user1 = new User(11,"a",111,"a");
userMapper.saveUser(user1);
this.B();
return 0;
}
@Override
public int B() {
User user2 = new User(11,"b",111,"b");
userMapper.saveUser(user2);
int i = 1 / 0;
return 0;
}
}
7,场景七,同一个Service内方法调用私有的方法C,当@Transactional 注解作用在方法A上时,事务起作用,数据回滚,代码如下。
@Service
@Slf4j
public class ComeServiceImpl implements ComeService {
@Autowired
UserMapper userMapper;
@Override
@Transactional
public int A() {
User user1 = new User(11,"a",111,"a");
userMapper.saveUser(user1);
this.C();
return 0;
}
private int C() {
User user2 = new User(11,"b",111,"b");
userMapper.saveUser(user2);
int i = 1 / 0;
return 0;
}
@Override
public int B() {
return 0;
}
}
8,场景八,同一个Service内方法调用私有的方法C,当@Transactional 注解作用在方法C上时,事务不起作用,方法A中的数据提交,方法C中的数据提交,代码如下。
@Service
@Slf4j
public class ComeServiceImpl implements ComeService {
@Autowired
UserMapper userMapper;
@Override
public int A() {
User user1 = new User(11,"a",111,"a");
userMapper.saveUser(user1);
this.C();
return 0;
}
@Transactional
private int C() {
User user2 = new User(11,"b",111,"b");
userMapper.saveUser(user2);
int i = 1 / 0;
return 0;
}
@Override
public int B() {
return 0;
}
}
9,场景九,不同Service方法间调用,当@Transactional 注解作用在方法A上时,事务起作用,方法A中的数据回滚,方法saveClassInfo中的数据回滚,代码如下。
@Service
@Slf4j
public class ComeServiceImpl implements ComeService {
@Autowired
UserMapper userMapper;
@Autowired
ClassInfoService classInfoService;
@Override
@Transactional
public int A() {
User user1 = new User(11,"a",111,"a");
userMapper.saveUser(user1);
classInfoService.saveClassInfo();
return 0;
}
@Override
public int B() {
return 0;
}
}
@Service
public class ClassInfoServiceImpl implements ClassInfoService {
@Autowired
ClassInfoMapper classInfoMapper;
@Override
public int saveClassInfo() {
ClassInfo classInfo = new ClassInfo("c","c",69D);
classInfoMapper.saveClassInfo(classInfo);
int i = 1/0;
return 0;
}
}
10,场景十,不同Service方法间调用,当@Transactional 注解作用在方法saveClassInfo上时,事务对A不起作用,方法A中的数据提交,方法saveClassInfo数据回滚,代码如下。
@Service
@Slf4j
public class ComeServiceImpl implements ComeService {
@Autowired
UserMapper userMapper;
@Autowired
ClassInfoService classInfoService;
@Override
public int A() {
User user1 = new User(11,"a",111,"a");
userMapper.saveUser(user1);
classInfoService.saveClassInfo();
return 0;
}
@Override
public int B() {
return 0;
}
}
@Service
public class ClassInfoServiceImpl implements ClassInfoService {
@Autowired
ClassInfoMapper classInfoMapper;
@Override
@Transactional
public int saveClassInfo() {
ClassInfo classInfo = new ClassInfo("c","c",69D);
classInfoMapper.saveClassInfo(classInfo);
int i = 1/0;
return 0;
}
}
推荐阅读:
总结Spring事务注解@transactional无法生效的八个场景 | w3cschool笔记
事物传播行为
1.TransactionDefinition.PROPAGATION_REQUIRED
使用的最多的一个事务传播行为,我们平时经常使用的@Transactional注解默认使用就是这个事务传播行为。如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。也就是说:
- 如果外部方法没有开启事务的话,
Propagation.REQUIRED修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。 - 如果外部方法开启事务并且被
Propagation.REQUIRED的话,所有Propagation.REQUIRED修饰的内部方法和外部方法均属于同一事务 ,只要一个方法回滚,整个事务均回滚。
举个例子:如果我们上面的aMethod()和bMethod()使用的都是PROPAGATION_REQUIRED传播行为的话,两者使用的就是同一个事务,只要其中一个方法回滚,整个事务均回滚。
@Service
Class A {
@Autowired
B b;
@Transactional(propagation = Propagation.REQUIRED)
public void aMethod {
//do something
b.bMethod();
}
}
@Service
Class B {
@Transactional(propagation = Propagation.REQUIRED)
public void bMethod {
//do something
}
}
2.TransactionDefinition.PROPAGATION_REQUIRES_NEW
创建一个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部方法是否开启事务,Propagation.REQUIRES_NEW修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。
举个例子:如果我们上面的bMethod()使用PROPAGATION_REQUIRES_NEW事务传播行为修饰,aMethod还是用PROPAGATION_REQUIRED修饰的话。如果aMethod()发生异常回滚,bMethod()不会跟着回滚,因为 bMethod()开启了独立的事务。但是,如果 bMethod()抛出了未被捕获的异常并且这个异常满足事务回滚规则的话,aMethod()同样也会回滚,因为这个异常被 aMethod()的事务管理机制检测到了。
@Service
Class A {
@Autowired
B b;
@Transactional(propagation = Propagation.REQUIRED)
public void aMethod {
//do something
b.bMethod();
}
}
@Service
Class B {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void bMethod {
//do something
}
}
3.TransactionDefinition.PROPAGATION_NESTED:
如果当前存在事务,就在嵌套事务内执行;如果当前没有事务,就执行与TransactionDefinition.PROPAGATION_REQUIRED类似的操作。也就是说:
- 在外部方法开启事务的情况下,在内部开启一个新的事务,作为嵌套事务存在。
- 如果外部方法无事务,则单独开启一个事务,与
PROPAGATION_REQUIRED类似。
这里还是简单举个例子:如果 bMethod() 回滚的话,aMethod()不会回滚。如果 aMethod() 回滚的话,bMethod()会回滚。
@Service
Class A {
@Autowired
B b;
@Transactional(propagation = Propagation.REQUIRED)
public void aMethod {
//do something
b.bMethod();
}
}
@Service
Class B {
@Transactional(propagation = Propagation.NESTED)
public void bMethod {
//do something
}
}
4.TransactionDefinition.PROPAGATION_MANDATORY
如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)
这个使用的很少,就不举例子来说了。
若是错误的配置以下 3 种事务传播行为,事务将不会发生回滚,这里不对照案例讲解了,使用的很少。
TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。TransactionDefinition.PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。
工作机制
@Transactional 的工作机制是基于 AOP 实现的,AOP 又是使用动态代理实现的。如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理,如果目标对象没有实现了接口,会使用 CGLIB 动态代理。
#参考文章:
1.spring中@Transactional注解的作用,使用场景举例_transactional注解作用_lq程序tomcat的博客-CSDN博客
2.spring源码阅读--@Transactional实现原理_@transactional注解原理_一撸向北的博客-CSDN博客