• Spring事务管理笔记


    事务的目的就是要保证数据的高度完整性和一致性。

    在实际的项目中,大多都是使用注解的方式来实现事物,这里也就简单记录下使用@Transactional方法和注意事项.

    1. 在xml中添加配置

      1
      2
      3
      4
      5
      6
      7
      <!-- TransactionManager -->
      <bean id="transactionManager"
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <property name="dataSource" ref="dataSource" />
      </bean>
      <!-- 如果没有它,@Transactional不会生效 -->
      <tx:annotation-driven transaction-manager="transactionManager"/>
    2. 使用
      在接口或类的声明处 ,写一个@Transactional 如:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
        // 业务方法需要在一个事物中运行,如果方法运行时,已经存在一个事物中,  
      // 那么加入该事物,否则为自己创建一个新事物。(默认)
      @Transactional(propagation = Propagation.REQUIRED)
      public void test1() {
      }

      // 声明方法不需要事务,如果方法没有关联到一个事务,容器不会为它开启事物。
      // 如果方法在一个事物中被调用,该事物会被挂起,在方法调用结束后,原先的
      // 事物便会恢复执行。
      @Transactional(propagation = Propagation.NOT_SUPPORTED)
      public void test2() {
      }
      // 表明不管是否存在事物,业务方法总会为自己发起一个新事物。
      // 如果方法已经运行在一个事物中,则原有事物会被挂起,
      // 新的事物会被创建,直到方法执行结束,新事物才算结束,
      // 原先的事务才会被恢复执行。
      @Transactional(propagation = Propagation.REQUIRES_NEW)
      public void test3() {
      }
      // 该属性指定业务方法只能在一个已经存在的事物中执行,
      // 业务方法不能发起自己的事物,如果业务方法在没有事物的环境
      // 下调用,容器就会抛出异常。
      @Transactional(propagation = Propagation.MANDATORY)
      public void test4() {
      }
      // 这个事物属性表明,如果业务方法在某个事务范围内被调用,则方法成为该事物的一部分,
      // 如果业务方法在事务范围外被调用,则方法在没有事物的环境下执行。
      @Transactional(propagation = Propagation.SUPPORTS)
      public void test5() {
      }
      // 指定业务方法绝对不能在事物范围内执行。如果业务方法在某个事物中执行,
      // 容器会抛出异常,只有业务方法没有关联到任何事物,才能正常执行。
      @Transactional(propagation = Propagation.NEVER)
      public void test6() {
      }
      // 如果一个活动的事物存在,则运行在一个嵌套的事物中,如果没有活动事物,
      // 则按REQUIRED属性执行,它使用了一个单独的事物,这个事物拥有多个回滚的保存点,
      // 内部事务的回滚不会对外事物造成影响,它只对DataSourceTransactionManager
      // 事务管理器起效。
      @Transactional(propagation = Propagation.NESTED)
      public void test7() {
      }
      @Transactional(isolation = Isolation.DEFAULT)
      public void test8() {
      }
      // 读已提交数据(会出现不可重复读和幻读)
      @Transactional(isolation = Isolation.READ_COMMITTED)
      public void test9() {
      }
      // 读未提交数据(会出现脏读、不可重复读和幻读)
      @Transactional(isolation = Isolation.READ_UNCOMMITTED)
      public void test10() {
      }
      // 可重复读(会出现幻读)
      @Transactional(isolation = Isolation.REPEATABLE_READ)
      public void test11() {
      }
      // 串行化
      @Transactional(isolation = Isolation.SERIALIZABLE)
      public void test12() {
      }
      // 抛出Exception异常时,记录回滚
      @Transactional(rollbackFor = Exception.class)
      public void test13() throws Exception {
      }
    3. 参数说明
      readOnly: 该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。例如:@Transactional(readOnly=true),要想只读生效,必须启动一个事务。而恰恰我们只是读取时,根本不需要事物,所以这个标识有点无意义. 

      rollbackFor: 该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如:
      指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)
      指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})

      rollbackForClassName: 该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。例如:
      指定单一异常类名称:@Transactional(rollbackForClassName=”RuntimeException”)
      指定多个异常类名称:@Transactional(rollbackForClassName={“RuntimeException”,”Exception”}) 

      noRollbackFor: 该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。例如:
      指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class)
      指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class}) 

      noRollbackForClassName: 该属性用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。例如:
      指定单一异常类名称:@Transactional(noRollbackForClassName=”RuntimeException”)
      指定多个异常类名称:
      @Transactional(noRollbackForClassName={“RuntimeException”,”Exception”}) propagation:该属性用于设置事务的传播行为,例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true) 

      isolation: 该属性用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置 

      timeout: 该属性用于设置事务的超时秒数,默认值为-1表示永不超时

    4. 注意

    • @Transactional 只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能.
    • 用 Spring 事务管理器,由spring来负责数据库的打开,提交,回滚.默认遇到运行期例外(throw new RuntimeException(“注释”);)会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到需要捕获的例外(throw new Exception(“注释”);)不会回滚,需我们以.指定方式来让事务回滚。
    • @Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。然而,请注意仅使用 @Transactional 注解不足于开启事务行为,它仅仅 是一种元数据,能够被可以识别 @Transactional 注解和上述的配置适当的具有事务行为的beans所使用。上面的例子中,其实正是 元素的出现 开启 了事务行为。
    • Spring团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。你当然可以在接口上使用 @Transactional 注解,但是这将只能当你设置了基于接口的代理时它才生效。因为注解是 不能继承 的,这就意味着如果你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装(将被确认为严重的)。因 此,请接受Spring团队的建议并且在具体的类上使用 @Transactional 注解。
    • 在单独使用不带任何参数的 @Transactional 注释时,传播模式默认设置为 REQUIRED,只读标志设置为 false,事务隔离级别设置为 READ_COMMITTED,而且事务不会针对受控异常(checked exception)回滚。

    遇到的坑

    在一个方法中,我们需要统计商品每天的销量并保存和更新商品的总销量,如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Transactional
    public ResultBase executeSalesStat(){
    try {
    //保存每日商品销量信息
    productDaySalesStatDAO.saveProductSalesStat();
    //更新商品总销量
    productDaySalesStatDAO.updateProductSalesStat();
    return new ResultBase();
    } catch (Exception e) {
    throw new Exception(e);
    }
    }

    假设我们在执行更新updateProductSalesStat();方法时出现了异常DataIntegrityViolationException,结果就是每日商品销量信息保存成功,而更新商品总销量没有成功,说明事物没有达到我们想要的结果,回滚保存每日销量。
    解决办法想到了两个:
    1.使用rollbackFor 参数,指定要回滚的异常:@Transactional(rollbackFor = Exception.class)
    2.抛出运行时异常,即:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    try {
    //保存每日商品销量信息
    productDaySalesStatDAO.saveProductSalesStat();
    //更新商品总销量
    productDaySalesStatDAO.updateProductSalesStat();
    return new ResultBase();
    } catch (RuntimeException e) {
    throw new RuntimeException(e);
    }

    参考IBM Spring 事务策略

    一个点赞,一个评论,既是肯定,又是鼓励!期待和你一起交流学习、共同进步!
    微信搜索公众号“jinglingwangcoding”或扫描下方二维码,一起交流
  • 相关阅读:
    live555学习之RTSP连接建立以及请求消息处理过程
    RTSP协议学习笔记
    RTP头结构解析
    Doubango ims 框架 分析之 多媒体部分
    VMware虚拟机 NAT模式 配置静态ip
    计算机网络: IP地址,子网掩码,网段表示法,默认网关,DNS服务器详解
    VMWare VMNet 8 的配置使用
    VMware虚拟机CentOS7
    如何禁止虚拟机自动获取DHCP分配的ip地址
    Linux之VMware虚拟机取消DHCP
  • 原文地址:https://www.cnblogs.com/admol/p/5101729.html
Copyright © 2020-2023  润新知