乐趣区

关于java:Spring阿里面试官问我事务注解Transactional失效场景有哪些

前言

Spring 事务管理是 Spring 的重点,也是开发利用中不可或缺的技能。相熟事务回滚机制及生效场景很是必要。

场景 1:不应用 @Transactional 注解

此时 事务必定不会回滚
如下代码所示,在插入数据之后紧接着执行了一个会触发 NPE(空指针异样)的操作。能够发现日志打印出了 ” 新增用户信息时捕捉到异样 ”,然而数据还是失常插入到了 MySQL 中,即不会产生回滚。

    @Override
    public void insertUser(String userId, String username) {
        try {String sql = "insert into `cps_user_info` (`user_id`,`user_name`) values (?,?);";
            jdbcTemplate.update(sql, userId, username);
            // 手动触发一个 NPE 异样
            List<Integer> ids = null;
            ids.add(1);
        } catch (Exception e) {throw new RuntimeException("新增用户信息时捕捉到异样");
        }
    }

场景 2:抛出受检异样但注解没有指定 rollbackFor

此时将 不会回滚 。实际上当咱们应用 @Transaction 时,默认为 RuntimeException(也就是运行时异样,即非受检异样)异样才会回滚。而 IOException 属于受检异样,当注解里不设置rollbackFor = Exception.class 时,该种状况就不会回滚事务。

    @Override
    @Transactional
    public void insertUser(String userId, String username) throws FileNotFoundException {String sql = "insert into `cps_user_info` (`user_id`,`user_name`) values (?,?);";
            jdbcTemplate.update(sql, userId, username);
            // 关上一个不存在的文件,将触发 FileNotFoundException
            FileInputStream file=new FileInputStream("user.txt");
    }

场景 3:增加了 @Transactional 然而 catch{}语句中未抛出异样

此时也 不会回滚事务
如下代码所示,尽管此时有注解了,然而 catch 体里没有抛出异样,此时事务也不会回滚。然而这个机制是什么呢?
实际上 Spring 的事务是通过 AOP 实现的,Spring 事务只在产生未被捕捉的 RuntimeException 时才回滚。Spring AOP 异样捕捉原理:被拦挡的办法需显式抛出异样,并且不能通过任何解决,这样 AOP 代理能力捕捉到办法的异样,进而进行回滚.

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertUser(String userId, String username) {
        try {String sql = "insert into `cps_user_info` (`user_id`,`user_name`) values (?,?);";
            jdbcTemplate.update(sql, userId, username);
            // 手动触发一个 NPE 异样
            List<Integer> ids = null;
            ids.add(1);
        } catch (Exception e) {}}

场景 4:增加了 @Transactional 且在 catch{}语句中显式的抛出异样

此时 事务失常回滚。且在日志中能看到打印出谬误日志。

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertUser(String userId, String username) {
        try {String sql = "insert into `cps_user_info` (`user_id`,`user_name`) values (?,?);";
            jdbcTemplate.update(sql, userId, username);
            // 手动触发一个 NPE 异样
            List<Integer> ids = null;
            ids.add(1);
        } catch (Exception e) {
            // 产生异样时将异样抛出,Spring  AOP 捕捉到异样后会触发事务回滚 
            throw new RuntimeException("新增用户信息时捕捉到异样");
        }
    }

场景 5:增加了 @Transactional 且在 catch{}语句中手动触发事务回滚

此时 事务失常回滚

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertUser(String userId, String username) {
        try {String sql = "insert into `cps_user_info` (`user_id`,`user_name`) values (?,?);";
            jdbcTemplate.update(sql, userId, username);
            // 手动触发一个 NPE 异样
            List<Integer> ids = null;
            ids.add(1);
        } catch (Exception e) {
            // 产生异样时手动触发事务回滚 
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }

能够看到,场景 4 和场景 5 都能够实现 MySQL 事务回滚。不同的是,场景 3 须要在管制层持续捕捉这个异样并解决。而场景 4 是手动回滚,这样管制层就无需去解决异样了。具体在开发中采取哪种回滚形式可依据理论场景衡量斟酌。

然而你认为这就完结了么?事件当然没有这么单纯。

场景 6:同一个代理类中的办法调用,被调用办法有 @Transactional 注解

如下代码所示,这种状况事务会回滚吗?答案是 不会
不仅不会,还会报出异样 org.springframework.transaction.NoTransactionException: No transaction aspect-managed TransactionStatus in scope。那么这是什么起因呢?事实上 Spring 中的 @Transactional 原理是通过TransactionInterceptor(事务拦截器)在指标办法执行前后进行拦挡。像如下的代码,Spring AOP 实际上是为每个@Service 注解的类生成一个代理类,因为代理类的 insertUser 办法没有被注解润饰,Spring 认为这里不必事务拦截器进行拦挡,即使被调用类中应用了事务注解。

    @Override
    public void insertUser(String userId, String username) {addUser(userId, username);
    }

    @Transactional(rollbackFor = Exception.class)
    public void addUser(String userId, String username){
        try {String sql = "insert into `cps_user_info` (`user_id`,`user_name`) values (?,?);";
            jdbcTemplate.update(sql, userId, username);
            // 手动触发一个 NPE 异样
            List<Integer> ids = null;
            ids.add(1);
        } catch (Exception e) {
            // 产生异样时手动触发事务回滚
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }

这里我想纠正一个误区,很多人认为 @Transactional(rollbackFor = Exception.class) 只有作用在 Service 层才会作用,在管制层应用不会走代理,就不会起作用,其实这是错的。在接口层应用该注解,条件满足的话一样会触发事务回滚。

小结

Spring 的事务根本是大厂面试很器重的知识点。不仅仅是事务的个性,事务流传行为这种实践面试题。对事务的理论利用也很器重。事实上少侠露飞在开发中发现很多人对于事务的应用就是加个 @Transactional(rollbackFor = Exception.class) 就完事的,但其实外面的逻辑可能会造成出现异常时事务并不会回滚。这还是很有危险的,极易造成数据的不统一。

我是少侠露飞,爱技术,爱分享。

退出移动版