新建的Spring Boot项目中,一般都会引用spring-boot-starter或者spring-boot-starter-web,而这两个起步依赖中都已经包含了对于spring-boot-starter-jdbcspring-boot-starter-data-jpa的依赖。 当我们使用了这两个依赖的时候,框架会自动默认分别注入DataSourceTransactionManagerJpaTransactionManager。 所以我们不需要任何额外配置就可以用@Transactional注解进行事务的使用。
    提示
    @Transactional注解只能应用到public可见度的方法上,可以被应用于接口定义和接口方法,方法会覆盖类上面声明的事务。
    例如用户新增需要插入用户表、用户与岗位关联表、用户与角色关联表,如果插入成功,那么一起成功,如果中间有一条出现异常,那么回滚之前的所有操作, 这样可以防止出现脏数据,就可以使用事务让它实现回退。
    做法非常简单,我们只需要在方法或类添加@Transactional注解即可。

    1. @Transactional
    2. public int insertUser(User user)
    3. {
    4. // 新增用户信息
    5. int rows = userMapper.insertUser(user);
    6. // 新增用户岗位关联
    7. insertUserPost(user);
    8. // 新增用户与角色管理
    9. insertUserRole(user);
    10. return rows;
    11. }
    • 常见坑点1:遇到检查异常时,事务开启,也无法回滚。 例如下面这段代码,用户依旧增加成功,并没有因为后面遇到检查异常而回滚!!
      @Transactional
      public int insertUser(User user) throws Exception
      {
        // 新增用户信息
        int rows = userMapper.insertUser(user);
        // 新增用户岗位关联
        insertUserPost(user);
        // 新增用户与角色管理
        insertUserRole(user);
        // 模拟抛出SQLException异常
        boolean flag = true;
        if (flag)
        {
            throw new SQLException("发生异常了..");
        }
        return rows;
      }
      

    原因分析:因为Spring的默认的事务规则是遇到运行异常(RuntimeException)和程序错误(Error)才会回滚。如果想针对检查异常进行事务回滚,可以在@Transactional注解里使用 rollbackFor属性明确指定异常。
    例如下面这样,就可以正常回滚:

    @Transactional(rollbackFor = Exception.class)
    public int insertUser(User user) throws Exception
    {
        // 新增用户信息
        int rows = userMapper.insertUser(user);
        // 新增用户岗位关联
        insertUserPost(user);
        // 新增用户与角色管理
        insertUserRole(user);
        // 模拟抛出SQLException异常
        boolean flag = true;
        if (flag)
        {
            throw new SQLException("发生异常了..");
        }
        return rows;
    }
    
    • 常见坑点2: 在业务层捕捉异常后,发现事务不生效。 这是许多新手都会犯的一个错误,在业务层手工捕捉并处理了异常,你都把异常“吃”掉了,Spring自然不知道这里有错,更不会主动去回滚数据。
      例如:下面这段代码直接导致用户新增的事务回滚没有生效。
      @Transactional
      public int insertUser(User user) throws Exception
      {
        // 新增用户信息
        int rows = userMapper.insertUser(user);
        // 新增用户岗位关联
        insertUserPost(user);
        // 新增用户与角色管理
        insertUserRole(user);
        // 模拟抛出SQLException异常
        boolean flag = true;
        if (flag)
        {
            try
            {
                // 谨慎:尽量不要在业务层捕捉异常并处理
                throw new SQLException("发生异常了..");
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
        return rows;
      }
      

    推荐做法:在业务层统一抛出异常,然后在控制层统一处理。

    @Transactional
    public int insertUser(User user) throws Exception
    {
        // 新增用户信息
        int rows = userMapper.insertUser(user);
        // 新增用户岗位关联
        insertUserPost(user);
        // 新增用户与角色管理
        insertUserRole(user);
        // 模拟抛出SQLException异常
        boolean flag = true;
        if (flag)
        {
            throw new RuntimeException("发生异常了..");
        }
        return rows;
    }
    

    Transactional注解的常用属性表:

    属性 说明
    propagation 事务的传播行为,默认值为 REQUIRED。
    isolation 事务的隔离度,默认值采用 DEFAULT
    timeout 事务的超时时间,默认值为-1,不超时。如果设置了超时时间(单位秒),那么如果超过该时间限制了但事务还没有完成,则自动回滚事务。
    read-only 指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。
    rollbackFor 用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。{xxx1.class, xxx2.class,……}
    noRollbackFor 抛出 no-rollback-for 指定的异常类型,不回滚事务。{xxx1.class, xxx2.class,……}
    ….

    提示
    事务的传播机制是指如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。 即:在执行一个@Transactinal注解标注的方法时,开启了事务;当该方法还在执行中时,另一个人也触发了该方法;那么此时怎么算事务呢,这时就可以通过事务的传播机制来指定处理方式。
    TransactionDefinition传播行为的常量:

    常量 含义
    TransactionDefinition.PROPAGATION_REQUIRED 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
    TransactionDefinition.PROPAGATION_REQUIRES_NEW 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
    TransactionDefinition.PROPAGATION_SUPPORTS 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
    TransactionDefinition.PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
    TransactionDefinition.PROPAGATION_NEVER 以非事务方式运行,如果当前存在事务,则抛出异常。
    TransactionDefinition.PROPAGATION_MANDATORY 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
    TransactionDefinition.PROPAGATION_NESTED 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。