spring事务传播嵌套总结

以下表格来自cnblogs

事务传播行为类型 说明
PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。支持嵌套事务的为JDBC 3.0以上的JDBC DataSourceTransactionManager 还有一些JTA proivder也支持,见org.springframework.jdbc.datasource.DataSourceTransactionManager

总结

1. 当外部方法无事务时:

//无事务
//UserService 调用 CommentService
public class UserService {
    .....
    public void save() throws Exception {
        jdbcTemplate.execute("insert into t_user(id,username,password) VALUES (6,'user1','password1')");
        commentService.save();
    }
    ...
}

//有事务(无论事务的传播方式是什么,REQUIRED,NESTED....)
public class CommentService {
    ....
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = {RuntimeException.class},readOnly = false)
    public void save() throws Exception {
        jdbcTemplate.execute("insert into t_comment(id,content) VALUES (2,'content1')");
        //throw new Exception();
    }
    ....
}

如上所示:UserService 会调用 CommentService,UserService的save方法没使用事务,CommentService的save方法使用了事务。
这种情况下:

1、如果UserService的save方法在调用CommentService的save方法之前执行失败并抛出异常,如果没有拦截此异常,则不再执行CommentService的save方法,这是显而易见的。如果拦截并处理了异常,则CommentService继续执行。

2、如果UserService的save方法在调用CommentService的save方法之前执行成功,继续执行CommentService的save方法,即使CommentService的save方法执行失败,==也不会影响之前的数据库操作==。

在外部方法(UserService.save())并未使用事务的情况下,内部方法(CommentService.save())法)不论有没有使用事务,都不会影响调用它之前的数据库操作。

2. 当外部方法使用事务时

//有事务
//UserService 调用 CommentService
public class UserService {
    .....
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = {RuntimeException.class},readOnly = false)
    public void save() throws Exception {
        //执行成功
        jdbcTemplate.execute("insert into t_user(id,username,password) VALUES (6,'user1','password1')");
        //执行失败
        commentService.save();
    }
    ...
}

//无论内部方法是否有事务
//1.不支持事务,直接抛出异常:PROPAGATION_NEVER
//2.新建事务,并挂起当前事务:PROPAGATION_REQUIRES_NEW
//3.嵌套事务:PROPAGATION_NESTED
//4.加入事务:PROPAGATION_REQUIRED,PROPAGATION_SUPPORTS
//5.以非事务方式执行,并挂起当前事务:PROPAGATION_NOT_SUPPORTED
public class CommentService {
    ....
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = {RuntimeException.class},readOnly = false)
    public void save() throws Exception {
        jdbcTemplate.execute("insert into t_comment(id,content) VALUES (2,'content1')");
        //throw new Exception();
    }
    ....
}

这种情况下:只要内部方法抛出会导致事务回滚的异常,那么就会影响外部事务,导致其回滚,当然,你可以捕获异常,这样就可以不影响外部事物了。在外部事物回滚的情况下,内部方法如果是使用的新建事务(PROPAGATION_REQUIRES_NEW)或以非事务方式执行(PROPAGATION_NOT_SUPPORTED),那么已经执行了的内部方法就不会随外部事物回滚。

PROPAGATION_NESTED和PROPAGATION_REQUIRES_NEW的区别

//有事务
//UserService 调用 CommentService
public class UserService {
    .....
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = {RuntimeException.class},readOnly = false)
    public void save() throws Exception {
        //执行成功
        jdbcTemplate.execute("insert into t_user(id,username,password) VALUES (6,'user1','password1')");
        //执行成功
        commentService.save();
        //回滚,抛出异常
        throw new RuntimeException()
    }
    ...
}

//无论内部方法是否有事务
//1.不支持事务,直接抛出异常:PROPAGATION_NEVER
//2.新建事务,并挂起当前事务:PROPAGATION_REQUIRES_NEW
//3.嵌套事务:PROPAGATION_NESTED
//4.加入事务:PROPAGATION_REQUIRED,PROPAGATION_SUPPORTS
//5.以非事务方式执行,并挂起当前事务:PROPAGATION_NOT_SUPPORTED
public class CommentService {
    ....
    @Transactional(propagation = Propagation.PROPAGATION_NESTED,rollbackFor = {RuntimeException.class},readOnly = false)
    public void save() throws Exception {
        jdbcTemplate.execute("insert into t_comment(id,content) VALUES (2,'content1')");
        //throw new Exception();
    }
    ....
}

当为PROPAGATION_NESTED时,如果内部方法执行成功,外部方法失败回滚,则会连同内部方法一起回滚。而PROPAGATION_REQUIRES_NEW时,内部方法则不会回滚(因为它是新建的事务)。

PROPAGATION_NESTED和PROPAGATION_REQUIRED的区别

//有事务
//UserService 调用 CommentService
public class UserService {
    .....
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = {RuntimeException.class},readOnly = false)
    public void save() throws Exception {
        //执行成功
        jdbcTemplate.execute("insert into t_user(id,username,password) VALUES (6,'user1','password1')");
        try{
           commentService.save();
        }catch(Exception e){
            
        }   
    }
    ...
}

//无论内部方法是否有事务
//1.不支持事务,直接抛出异常:PROPAGATION_NEVER
//2.新建事务,并挂起当前事务:PROPAGATION_REQUIRES_NEW
//3.嵌套事务:PROPAGATION_NESTED
//4.加入事务:PROPAGATION_REQUIRED,PROPAGATION_SUPPORTS
//5.以非事务方式执行,并挂起当前事务:PROPAGATION_NOT_SUPPORTED
public class CommentService {
    ....
    @Transactional(propagation = Propagation.PROPAGATION_NESTED,rollbackFor = {Exception.class},readOnly = false)
    public void save() throws Exception {
        jdbcTemplate.execute("insert into t_comment(id,content) VALUES (2,'content1')");
        throw new Exception();
    }
    ....
}

当内部方法的事务传播为PROPAGATION_NESTED时,将开始一个 "嵌套的" 事务, 它是已经存在事务的一个真正的子事务. 嵌套事务开始执行时, 它将取得一个 savepoint.如果这个嵌套事务失败, 我们将回滚到此 savepoint. 如果我们在外部方法中捕获异常,那么,外部的事务不会回滚(至少部分事务执行成功),仅仅"嵌套的" 事务回滚到savepoint上。但如果内部方法的事务传播为PROPAGATION_REQUIRED,即使我们在外部方法中捕获异常,外部的事务也会全部回滚(整个事务都不成功)。

如果你自己希望事务回滚,仅仅抛出会导致回滚的异常即可。默认是RuntimeException和Error。注意经过Spring封装的所有数据库异常都是RuntimeException的子类。也就是说都会导致回滚。如果你自定义的异常回滚,只需要在rollbackfor上添加就行了。自定义异常回滚的例子:如远程调用出错的情况下,如条件未满足,需要回滚等等

你可能感兴趣的:(spring事务传播嵌套总结)