• 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”或扫描下方二维码,一起交流
  • 相关阅读:
    康复计划
    Leetcode 08.02 迷路的机器人 缓存加回溯
    Leetcode 38 外观数列
    Leetcode 801 使序列递增的最小交换次数
    Leetcode 1143 最长公共子序列
    Leetcode 11 盛水最多的容器 贪心算法
    Leetcode 1186 删除一次得到子数组最大和
    Leetcode 300 最长上升子序列
    Leetcode95 不同的二叉搜索树II 精致的分治
    Leetcode 1367 二叉树中的列表 DFS
  • 原文地址:https://www.cnblogs.com/admol/p/5101729.html
Copyright © 2020-2023  润新知