事务(十九)


spring事务管理中,用try-catch处理了异常,事务也会回滚?

咱们知道在平时的开发中,若是在事务方法中开发人员本身用try-catch处理了异常,那么spring aop就捕获不到异常信息,从而会致使spring不能对事务方法正确的进行管理,不能及时回滚错误信息。

下面用代码演示一下:

@Override
    @Transactional(rollbackFor = Exception.class)
    public int doSaveUser() throws Exception {
        int result = 0;
        UserEntity u = new UserEntity();
        u.setUserSex("男");
        u.setUserName("AAA");
        try {
            result = userMapper.insertUser(u);
            int i = 1 / 0;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

控制台报错:

 数据库:

 但是,只要是咱们本身处理了异常,事务就必定不会回滚吗?答案是不必定的,下面用两段代码对比一下:

代码一:

public class User2ServiceImpl implements User2Service {
    @Autowired
    private UserService userService;
    @Autowired
    private UserMapper userMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int doSaveUser() throws Exception {
        int result = 0;
        UserEntity u = new UserEntity();
        u.setUserSex("男");
        u.setUserName("小A");
        userMapper.insertUser(u);
        try {
            u.setUserName("小B");
            result = userService.insertUser(u); //此时调用的方法没有加事务
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public int insertUser(UserEntity user) throws Exception {
        int i = 1 / 0;
        return userMapper.insertUser(user);
    }

}
异常信息:

 数据库:

 能够看到因为咱们本身处理了保存小B时抛出的异常,事务方法没有受到影响,依然正常的保存了小A,并无回滚事务。

代码二:

@Service
public class User2ServiceImpl implements User2Service {
    @Autowired
    private UserService userService;
    @Autowired
    private UserMapper userMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int doSaveUser() throws Exception {
        int result = 0;
        UserEntity u = new UserEntity();
        u.setUserSex("男");
        u.setUserName("小C");
        userMapper.insertUser(u);
        try {
            u.setUserName("小D");
            result = userService.insertUser(u); //此时调用的方法加上事务
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}



@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public int insertUser(UserEntity user) throws Exception {
        int i = 1 / 0;
        return userMapper.insertUser(user);
    }

}

异常信息:

 此时数据库里面一条记录也没有,也就是是说doSaveUser()方法也进行了事务回滚,咱们已经用try-catch处理了异常了,为何还会事务回滚呢?

咱们此时把insertUser方法稍微修改一下:

@Override
   @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public int insertUser(UserEntity user) throws Exception {
        int i = 1 / 0;
        return userMapper.insertUser(user);
    }

此时数据库多了一条记录:

 这里,我把spring事务传播机制从REQUIRED改为了REQUIRES_NEW,doSaveUser()方法就没有进行事务回滚了,到这里你应该能猜到了,spring事务传播机制默认是REQUIRED,也就是说支持当前事务,若是当前没有事务,则新建事务,若是当前存在事务,则加入当前事务,合并成一个事务,当insertUser方法有事务且事务传播机制为REQUIRED时,会和doSaveUser()方法的事务合并成一个事务,此时insertUser方法发生异常,spring捕获异常后,事务将会被设置全局rollback,而最外层的事务方法执行commit操做,这时因为事务状态为rollback,spring认为不该该commit提交该事务,就会回滚该事务,这就是为何doSaveUser()方法的事务也被回滚了。

下面咱们再看一下spring的事务传播机制:

1.REQUIRED (默认):支持当前事务,若是当前没有事务,则新建事务,若是当前存在事务,则加入当前事务,合并成一个事务,若是一个方法发生异常回滚,则整个事务回滚。

2.REQUIRES_NEW:新建事务,若是当前存在事务,则把当前事务挂起,这个方法会独立提交事务,不受调用者的事务影响,父级异常,它也是正常提交,但若是是此方法发生异常未被捕获处理,且异常符合父级事务方法回滚规则,则父级方法事务会被回滚。

3.NESTED(嵌套事务):若是当前存在事务,它将会成为父级事务的一个子事务,方法结束后并无提交,只有等父事务结束才提交,若是当前没有事务,则新建事务(此时,相似于REQUIRED ),若是它异常,它自己进行事务回滚,父级能够捕获它的异常而不进行回滚,正常提交,但若是父级异常,它必然回滚。

4.SUPPORTS:若是当前存在事务,则加入事务,若是当前不存在事务,则以非事务方式运行。

5.NOT_SUPPORTED:以非事务方式运行,若是当前存在事务,则把当前事务挂起。

6.MANDATORY:若是当前存在事务,则运行在当前事务中,若是当前无事务,则抛出异常,即父级方法(调用此方法的方法)必须有事务。

7.NEVER:以非事务方式运行,若是当前存在事务,则抛出异常,即父级方法必须无事务。

我经过自己实验的理解:(以下只涉及存在父级事务和子级事务时,子级各种四种事务级别的区别)

  1. REQUIRED SUPPORTS

    父方法存在事务,子方法是这两种级别时:

    子方法事务相当于跟父方法共用一个事务,只要子方法抛出异常,不管父方法有没有捕获异常,父方法都会进行回滚,可以理解为在同一个事务周期,子方法commit,父方法才能commit,即使父方法对子方法异常进行了捕获,吞掉了,那子方法里面异常回滚后,相当于子方法没有commit,那么父方法即使不报错,也会进行回滚(不会commit),当然该种情况共用一个事务,如果子方法不报错正常commit,但是父方法报错回滚了,子方法也会进行回滚。

  2. REQUIRES_NEW

    父方法存在事务,子方法是该级别时:

    子方法事务和父方法事务不是同一个,那么子方法抛出异常,不会commit,如果父方法没有捕捉异常,相当于父方法也抛出了异常,父方法也不会commit;如果父方法捕捉了子方法异常,则父方法相当于没有报错,会进行commit;

  3. NESTED(嵌套事务)

    父方法存在事务,子方法是该级别时:

    当父方法有事务,子方法会新增子事务,和父事务并不是同一个,这中情况就跟REQUIRES_NEW一样,只是有一个区别,如果子方法没异常,父方法有异常,父方法回滚,同时子方法也会回滚,这就是嵌套事务,而REQUIRES_NEW不会这样;

总结:其实很好理解,就看方法之间事务是不是同一个就行了,同一个事务就会同时回滚,不是同一个事务就不会相互影响(嵌套事务特殊,父事务会影响子事务),而REQUIRED就是同一个事务,不管有没有捕获都会回滚。

转载:http://www.javashuo.com/article/p-uarqxbqy-b.html

可参考进行验证:https://www.jianshu.com/p/8beab9f37e5b

TRANSLATE with x English
Arabic Hebrew Polish
Bulgarian Hindi Portuguese
Catalan Hmong Daw Romanian
Chinese Simplified Hungarian Russian
Chinese Traditional Indonesian Slovak
Czech Italian Slovenian
Danish Japanese Spanish
Dutch Klingon Swedish
English Korean Thai
Estonian Latvian Turkish
Finnish Lithuanian Ukrainian
French Malay Urdu
German Maltese Vietnamese
Greek Norwegian Welsh
Haitian Creole Persian  
Bing Webmaster Portal Back