Spring 中的事务注解 @Transactional

@Transactional 特性

  1. @Transactional注解只能应用到 public 修饰符上,对其他修饰符不起作用,但不报错。
  2. @Transactional 一般加到实现类或实现类方法上,不要加到接口或接口方法上。
  3. @Transactional仅仅对 unchecked 异常进行事务异常回滚;如果是 checked 异常则不进行异常回滚。
    • unchecked 异常一般为错误或运行时异常
      • Error:Throwable 的子类。著名的 VirtualMachineError 是 Error 的子类之一,VirtualMachineError 还有 StackOverflowError、OutOfMemoryError 等子类。
      • RuntimeException:Exception 的子类,而 Exception 同样是 Throwable 的子类。RuntimeException 下面包括 NPE,ClassCastException,ArithmaticException 等异常,这些错误的特点是不用被显式地抛出或捕获,开发者通过仔细检查代码可以避免这些异常。
    • checked 异常则为编译过程中的异常
      • 其他 Exception:除了 RuntimeException 之外其他的 Exception 的子类,包括 FileNotFoundException,IOException,SQLException 等,这些异常的特点是必须被代码抛出或捕获,否则编译都无法通过。

@Transactional 有效场景

  1. 正常执行写操作

    @Override
    @Transactional
    public int updateById(Employee employee, Integer empId) {
        return employeeMapper.updateById(employee, empId);
    }
    
  2. unchecked 异常

    • RuntimeException

      @Override
      @Transactional
      public int updateByIdRuntimeException(Employee employee, Integer empId) {
          int effect = employeeMapper.updateById(employee, empId);
          System.out.println(1 / 0);
          return effect;
      }
      

      调用此方法,在执行此方法的语句System.out.println(1 / 0);时会抛出运行时异常中的算术异常(ArithmaticException),事务会回滚,数据库中的数据不会被改变。

    • OutOfMemoryError

      写一个类来制造堆内存溢出

      public class HeapOOM {
      
          static class OOMObject {
          }
      
          public static void mockHeapOOM() {
              List<Object> objects = new ArrayList<>();
      
              while (true) {
                  objects.add(new OOMObject());
              }
          }
      }
      

      可以调用此类的静态方法 mockHeapOOM 不停地创建 OOMObject 对象并将其添加至数组中,而被数组强引用的对象无法被 GC 回收,堆内存很快被消耗完毕。

      @Override
      @Transactional
      public int updateByIdOOM(Employee employee, Integer empId) {
          int effect = employeeMapper.updateById(employee, empId);
          HeapOOM.mockHeapOOM();
          return effect;
      }
      

      打开 IntelliJ IDEA 的 Edit Configuration,在 VM Options 一栏为调用 updateByIdOOM 方法的单元测试配置好 JVM 参数。

      -ea -Xmx10m -Xms5m  -XX:+HeapDumpOnOutOfMemoryError
      
      • -ea 可以打开断言机制
      • -Xmx10m -Xms5m 表示堆最大可用值是 10M,堆初始值为 5M
      • -XX:+HeapDumpOnOutOfMemoryError 打印堆溢出报错信息

      运行单元测试方法,控制台中报如下错误信息:

      java.lang.OutOfMemoryError: GC overhead limit exceeded
      

      查看数据库,数据没有被改变,所以此时事务正常回滚。

@Transactional 无效场景

  1. checked 异常

    扫描二维码关注公众号,回复: 9233797 查看本文章
    @Override
    @Transactional
    public void updateByIdException(Employee employee, Integer empId) throws IOException {
        employeeMapper.updateById(employee, empId);
        throw new IOException("模拟 IO 错误");
    }
    

    调用此方法,会在throw new IOException("模拟 IO 错误")时抛出模拟的错误,但是事务并不回滚,数据库中的数据发生了改变。这是因为 IOException 属于 checked 异常,@Transactional 默认情况下不对 checked 异常进行事务回滚。此时,想要在抛出 checked 异常时进行事务回滚,需要在 @Transactional后面配置上rollBackFor属性:

    @Override
    @Transactional(rollbackFor = IOException.class)
    public void updateByIdException(Employee employee, Integer empId) throws IOException {
        employeeMapper.updateById(employee, empId);
        throw new IOException("模拟 IO 错误");
    }
    

    这里配置为rollbackFor = Exception.class可以。

  2. 被没有事务控制的方法调用

    如果带有 @Transactional的方法 A 被另一个不带有@Transactional的方法 B 调用,那么调用 B 时 A 的@Transactional也会失效。

    @Override
    @Transactional
    public int updateByIdRuntimeException(Employee employee, Integer empId) {
        int effect = employeeMapper.updateById(employee, empId);
        System.out.println(1 / 0);
        return effect;
    }
    
    @Override
    public int updateByIdWithoutTransactional(Employee employee, Integer empId) {
        return updateByIdRuntimeException(employee, empId);
    }
    
    

    此时,调用 updateByIdWithoutTransactional,数据库的数据会被改变,这是由于事务不会因为在updateByIdRuntimeException中抛出 ArithmaticException而回滚。

发布了83 篇原创文章 · 获赞 333 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/qq_15329947/article/details/104141759