关于spring的Transactional事务的传播propagation特性

 

传播行为:当内部事务方法被当前事务方法(外部事务)调用时,必须指定内部事务应该如何传播。例如:方法可能继续在外部事务中运行,也可能开启一个新事务,并在自己的事务中运行。

关于事务回滚机制,我个人认为主要是看异常发生点的位置,异常发生时,还未提交的事务都会被回滚,还未运行的事务也不会被执行;而已经提交了的则不会被回滚。

事务传播行为定义内部事务如何在外部事务中运行

注意,在同一个Service中的方法互相调用时不存在事务传播,事务特性以当前事务为准(Spring同一个Bean中方法的互相调用中被调用方法的注解会失效)。只有在两个不同的Service之间调用才会有事务的传播特性。

Spring基于注解@Transactional的属性propagation属性有以下七种:

REQUIRED

默认的传播行为,如果外部事务存在,则内部事务将会在该事务中运行;否则,会启动一个新的事务。

内部方法必须在事务中运行。

REQUIRES_NEW

不论外部事务是否存在,内部事务总会开启新的事务,如果有外部事务,则将其挂起,当内部事务完成后再继续外部事务。

内部方法必须在“新事务”中运行。

SUPPORTS

如果存在一个外部事务,则内部方法在外部事务中运行。如果没有外部事务,则以非事务的执行。

内部方法不必保证一定要在事务中运行。

NOT_SUPPORTED

 内部方法总是非事务地执行,并挂起任何存在的外部事务。不支持事务

MANDATORY

如果已经存在一个外部事务,则内部方法在外事务中运行。如果外部事务,则抛出异常。

内部方法必须在外部事务中运行,否则抛出异常。强制事务

NEVER

总是非事务地执行,如果存在一个活动的外部事务,则抛出异常。

必须在非事务中运行,否则抛出异常。强制非事务

NESTED

如果当前已经存在一个外部事务,那么该方法将会以嵌套子事务的方式在外部事务中运行。

子事务回滚savepoint非事务回滚,在主事务完成后才会提交或者回滚。

示例:测试代码,内部事务插入角色,外部事务插入用户。

//测试
//正常操作会在数据库两个表中各插入一条记录
@SpringBootTest
@ComponentScan
class DemoApplicationTests {
    @Autowired
    UserService userService = null;
    @Test
    void contextLoads() {
        User user = new User();
        user.setName("张三");
        userService.addUser(user);
    }
}


@Service
public class RoleServiceImpl implements RoleService {
    @Autowired
    RoleDao roleDao = null;

    //内部事务
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void addRole(Role role) {
        roleDao.addRole(role);
        throw new RuntimeException("内部事务异常");
    }
}

@Service
public class UserServiceImpl implements UserService {
     @Autowired
    UserDao userDao = null;
     @Autowired
    RoleService roleService = null;

    //外部事务
    @Transactional
    @Override
    public void addUser(User user) {
        userDao.addUser(user);
        Role role = new Role();
        role.setRname("人事部");
        roleService.addRole(role);//调用RoleService的方法
        throw new RuntimeException("外部事务异常");
    }

}

一、REQUIRED

内部事务传播特性为REQUIRED时:在外部事务中运行或者开启新事务。

  • 外部事务存在时:当外部方法或者内部方法发生异常时,因为内部事务在外部事务中运行,所以两条插入操作都会回滚,数据不会被持久化操作,因为他们是同一条事务

  • 外部事务不存在时:外部方法异常时,对两条操作都无影响,外部方法是在无事务中运行的;而内部方法是在内部事务中运行的,内部方法异常时,内部方法是在事务中运行,因此会回滚,而外部方法是无事务的,所以不会影响插入。

 

二:REQUIRES_NEW

内部事务传播特性为REQUIRES_NEW时:不论是否存在外部事务,都会开启新事务。

  • 外部事务存在时:当外部方法产生异常时,外部事务回滚,内部事务正常提交;当内部方法产生异常时,内部事务回滚,同时异常抛至外部,因此也会回滚外部事务。他们是两条事务,内部事务运行时,外部事务是被挂起的,当内部事务完成后继续外部事务。

  • 外部事务不存在时:外部方法异常时,对两条操作都无影响,外部方法是在无事务中运行的;而内部方法是在内部事务中运行的,内部方法异常时,内部方法是在事务中运行,因此会回滚,而外部方法是无事务的,所以不会影响插入。

三:SUPPORTS

内部事务传播特性为SUPPORTS时:如果存在一个外部事务,则内部方法在外部事务中运行。如果没有外部事务,则以非事务的执行。

  • 外部事务存在时:当外部方法或者内部方法发生异常时,因为内部事务在外部事务中运行,所以两条插入操作都会回滚,数据不会被持久化操作,因为他们是同一条事务

  • 外部事务不存在时:内外方法均在无事务中运行。

四、NOT_SUPPORTED

内部事务传播特性为NOT_SUPPORTED时:内部方法总是非事务地执行,并挂起任何存在的外部事务

  • 外部事务存在时:当外部方法异常时,回滚外部事务,而内部是非事务的,因此对内部事务不影响;当内部方法异常时,内部是非事务的,因此不会回滚内部事务,异常会抛至外部,回滚外部事务

  • 外部事务不存在时:内外方法均在无事务中运行。

 

五、MANDATORY

内部事务传播特性为MANDATORY时:如果已经存在一个外部事务,则在外部事务中运行。如果没有外部事务,则抛出异常(强制事务)

  • 外部事务存在时:当外部方法或者内部方法发生异常时,因为内部事务在外部事务中运行,所以两条插入操作都会回滚,数据不会被持久化操作,因为他们是同一条事务

  • 外部事务不存在时:异常:IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'  and 外部事务会正常提交。

六、NEVER

内部事务传播特性为NEVER时:总是非事务地执行,如果存在一个活动的外部事务,则抛出异常(强制非事务)

  • 外部事务存在时:异常:IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'   内外事务均不会提交。

  • 外部事务不存在时:内外方法均在无事务中运行。

 

七、NESTED

内部事务传播特性为NESTED时:内部开启一个可嵌套的事务,它是已经存的外部事务的一个真正的子事务。

潜套事务开始执行时,  它将取得一个 savepoint,如果这个嵌套事务失败,我们将回滚到此 savepoint(非事务回滚),潜套事务是外部事务的一部分,只有外部事务结束后它才会被提交或回滚。

  • 外部事务存在时:内部方法异常会回滚至savepoint,同时异常抛至外部,然后才是主事务的rollback;外部方法异常时,会回滚主事务(包括子事务的事务回滚)。

  • 外部事务不存在时:相当于REQUIRES_NEW,内部为一个新事务。

       

八、关于REQUIRES_NEW和NESTED的区别

NESTED子事务的回滚只是回滚至保存点,而非事务回滚,在主事务完成后才会被提交或者回滚。

关于spring的Transactional事务的传播propagation特性_第1张图片

 

你可能感兴趣的:(spring)