@Transactional注解解析

284 阅读7分钟

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;
    }
}

image.png

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 动态代理。

image.png

#参考文章:

1.spring中@Transactional注解的作用,使用场景举例_transactional注解作用_lq程序tomcat的博客-CSDN博客

2.spring源码阅读--@Transactional实现原理_@transactional注解原理_一撸向北的博客-CSDN博客

3.Spring 事务详解 | JavaGuide(Java面试 + 学习指南)