zoukankan      html  css  js  c++  java
  • spring事务传播机制的测试结果

    /**
         * @Component是个一般性的注解,使用此注解修饰的POJO类,有value属性,指定bean的id。也可不写。默认值是类名首字母小写
         * @Resource是控制依赖注入的,@Resource有两个属性是比较重要的,分是name和type。设置那个属性,按那个策略注入。不设置,默认按ByName策略注入。
         * @Autowired是控制依赖注入。按byType自动注入。
         *
         * @Controller是springMVC中注解控制器的。
         * @RequestMapping是SpringMVC中注解分发器。
         *
         * spring的事务注解:
         * @Transactional 只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能.
         * 如:@Transactional(rollbackFor=Exception.class)可以使checked异常发生时,数据库操作也rollback、
         *  :@Transactional(noRollbackFor=RuntimeException.class)可以使unchecked异常发生时也提交数据库操作。
         *  
         *  
         * 读取数据库中的数据时是不需要事务管理的,这种情况下可以使用事务的传播行为来告诉Spring不需要开启事务,
            如:@Transactional(propagation = Propagation.NOT_SUPPORTED)。
            
          Spring 提供的几种事务控制

    1.PROPAGATION_REQUIRED(加入已有事务)
        如果当前运行环境存在事务,则融合到当前事务中。
       如果没有则开启一个新的事务(融合成一个的事务)。

    2.RROPAGATION_REQUIRES_NEW(独立事务)
         如果当前运行环境存在事务,就挂起当前事务,新建一个事务运行。新建事务提交或回滚后,恢复当前事务。
       如果当前运行环境中不存在事务。就新建一个事务运行。
       新建事务和当前事务没有关系,互不影响。(你的提交和回滚和我没一点关系。在当前事务中抓取了新建事务的异常),使用不同的数据库连接。
        

    3.PROPAGATION_NESTED(嵌套事务)    
         如果当前运行环境存在事务,新开启一个子事务(savepoint)。嵌套事务运行。
         如果当前运行环境没有事务,则新建一个事务。类似PROPAGATION_REQUIRED。
         父事务影响子事务。子事务提交,只有父事务也提交。子事务才算真正提交。
         子事务不影响父事务(在当前事务中抓取子事务异常的情况下)



    4.PROPAGATION_SUPPORTS(跟随环境)
        如当前运行环境存在事务,就使用当前事务运行。
       如当前运行环境不存在事务,就以非事务方式执行。

    5.PROPAGATION_NOT_SUPPORTED(非事务方式)
        如当前运行环境存在事务,则将这个事务挂起,使用新的数据库连接,以非事务方式运行。
       如当前运行环境不存在事务,就以非事务的方式运行。

    6.PROPAGATION_NEVER(排除事务)
        如果当前运行环境存在事务,就抛出异常。
        如果当前运行环境不存在事务,就以非事务的方式运行。

    7.PROPAGATION_MANDATORY(需要事务)
          如果当前运行环境存在事务,就加入当前事务中运行。
        如果当前运行环境不存在事务,就抛出异常。
        


                       
            事务的隔离级别

              使用@Transactional的Isolation属性可以指定事务的隔离级别。但事务的隔离级别是由底层的数据库实现的,并不是由Spring来实现。
            
            1.READ_UNCOMMITTED:会出现脏读、不可重复读和幻读问题;

            2.READ_COMMITTED:会出现不可重复读和幻读问题;

            3.REPEATABLE_READ:会出现幻读问题;
            
            4.SERIALIZABLE:串行化,不会出现上面的问题。

              一般的数据库默认提供的是READ_COMMITTED隔离级别,如sqlserver2000;Mysql默认提供的是REPEATABLE_READ。
            
            
            @Transactional  的所有可选属性如下:
                    属性                 类型                 默认值                 说明
                propagation        Propagation枚举     REQUIRED      事务传播属性
                isolation          isolation枚举          DEFAULT           事务隔离级别
                readOnly           boolean         false            是否只读
                timeout            int              -1                        超时(秒)
                rollbackFor        Class[]           {}          需要回滚的异常类
                rollbackForClassName String[]        {}          需要回滚的异常类名
                noRollbackFor        Class[]           {}          不需要回滚的异常类
                noRollbackForClassName String[]       {}          不需要回滚的异常类名
                
            //事务传播属性
        @Transactional(propagation=Propagation.REQUIRED) //如果有事务,那么加入事务,没有的话新建一个(不写的情况下)
        @Transactional(propagation=Propagation.NOT_SUPPORTED) //容器不为这个方法开启事务
        @Transactional(propagation=Propagation.REQUIRES_NEW) //不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
        @Transactional(propagation=Propagation.MANDATORY) //必须在一个已有的事务中执行,否则抛出异常
        @Transactional(propagation=Propagation.NEVER) //必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
        @Transactional(propagation=Propagation.SUPPORTS) //如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.
        @Transactional(propagation=Propagation.NESTED)
        @Transactional (propagation = Propagation.REQUIRED,readOnly=true) //readOnly=true只读,不能更新,删除
        @Transactional (propagation = Propagation.REQUIRED,timeout=30)//设置超时时间
        @Transactional (propagation = Propagation.REQUIRED,isolation=Isolation.DEFAULT)//设置数据库隔离级别





        验证spring的7中事务传播机制:
            【一】:Propagation.REQUIRED --->如果当前运行环境有事务,则加入该事务,融合成一个完整的大事务。如果没有事务,则新建一个事务。
                    示例(1):打赏一篇博客。
                    步骤:------blogService中的awardBlog()方法 REQUIRED
                        (1)打赏作者账户减去打赏钱数 REQUIRED
                        (2)被打赏的博客加上打赏钱数 REQUIRED
                        (3)被打赏的作者账户加上钱数 REQUIRED
                    结果:如果步骤(1)(2)(3)中任何一个对数据库的操作抛出异常,则整个操作业务,相关数据回滚。
                         如果步骤(1)(2)(3)全部操作成功,未抛出异常,则整个操作业务,相关数据提交。
            
            
            【二】:Propagation.MANDATORY--->如果当前运行环境不存在事务,就抛出异常。如果存在事务,则加入事务运行。
                    示例(1):给当前登陆用户充值
                    步骤:---------authorService.payMoney()方法MANDATORY。
                        (1)控制层直接调用该业务方法,抛出异常。
                            
                    结果:异常:IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
                    
                    示例(2):给当前用户充钱,并给他的第一篇博客加相应的积分。
                    步骤:--------authorService.payAndaddMoney()方法REQUIRED
                         (1)给当前登陆用户充值authorService.payMoney()-->MANDATORY
                         (2)查询出当前登陆用户的博客集合blogService.query()--->SUPPORTS
                         (3)给当前登陆用户的第一篇博客加上积分blogService.addmoneyBlogById()--->REQUIRED
                
                    结果:(1)如果(1)处报异常,整个事务回滚。
                            (2)如果(3)处报异常,整个事务回滚。
                            综上:mandatory传播,当前运行环境不存在事务,抛出异常。如果存在,加入当前事务,并遵循当前事务传播的机制。
                           
                           
                           
            【三】PROPAGATION_NEVER--->如果当前存在事务则抛出异常,如果不存在事务则已非事务运行
                    示例(1):直接消减博客50积分
                    步骤:--------blogService.subtractBlogMoney()方法NEVER
                        (1)控制层直接调用该业务方法
                    结果:如果(1)顺利执行,虽然没有事务,但会做提交操作。
                        如果(1)与数据库交互后,抛出异常,同样会提交操作。数据不回滚。
                        
                        
                        
                    示例(2):先判断后消减
                    步骤:--------blogService.oneIfTwoSubtract()方法REQUIRED
                        (1)先查询指定id的博客的对象blogService.findBlogById()---->Propagation.REQUIRED,readOnly=true
                        (2)直接消减博客50积分blogService.subtractBlogMoney()--->NEVER
                    结果:(1)如果(1)顺利执行(2)抛出异常
                            org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'
                        
            
            
            
            【四】PROPAGATION_SUPPORTS--->是指 Spring 容器中如果当前没有事务存在,就以非事务方式执行;如果有,就使用当前事务。
                    示例(1):增加一篇博客
                    步骤:-------blogService.saveBlog()方法SUPPORTS
                        (1)控制层直接调用该业务方法
                    结果:(1)如果(1)顺利执行,数据会发生提交操作。
                        (2)如果(1)中与数据库交互后,抛出异常。数据也会发生提交操作,不回滚。无事务运行。
                    
                    示例(2):添加博客加积分
                    步骤:------blogService.addBlogAddMoney()方法REQUIRED
                            (1)给当前作者加100分积分authorService.addMoney()---->Propagation.REQUIRED
                            (2)添加博客blogService.saveBlog()---->Propagation.SUPPORTS
                     结果:(1)如果(1)(2)顺利执行完。(1)处操作发生提交(2)处操作发生提交
                          (2)如果(1)处执行(2)处数据库交互后,内部抛出抛异常  (1)处数据回滚(2)处数据回滚 。SUPPORTS跟随了事务的环境
                          (3)如果(1)(2)顺利执行,在addBlogAddMoney()最后抛出异常 (1)(2)处数据回滚。SUPPORTS跟随了事务的环境
                         
                         
                         
                         
             【五】PROPAGATION_NOT_SUPPORTED--->是指如果当前存在事务则将这个事务挂起,并使用新的数据库连接。新的数据库连接不使用事务。
                                             not_supported注解的方法执行完之后,释放无事务的数据库连接。恢复当前挂起的事务。
                     示例(1):修改博客内容
                     步骤:----blogService.updateBlog(Blog blog)方法NOT_SUPPORTED
                             (1)控制层直接调用该方法
                     结果:(1)如果正常执行(1)--数据提交。
                         (2)如果(1)中与数据库交互后,抛出异常。--数据提交,说明无事务运行
                         
                         
                     示例(2):修改博客内容的同时,并为当前作者加50分
                     步骤:----blogService.updateBlog2()方法propagation=Propagation.REQUIRED
                             (1)给当前作者加50分 authorService.addMoney(50, authorId);propagation=Propagation.REQUIRED
                             (2)修改博客内容blogService.updateBlog(Blog blog)propagation=Propagation.NOT_SUPPORTED
                     结果:(1)如果(1)(2)都顺利进行 --(1)提交(2)提交
                          (2)如果(1)顺利执行(2)交互后抛异常在业务方法无抓取异常--(1)回滚(2)交互提交 证明无事务
                                  验证:NOT_SUPPORTED让当前事务挂起,非事务运行,能提交数据。异常抛出,当前事务回滚.
                          (3)如果(1)顺利执行(2)抛异常,但被抓取--(1)提交(2)交互提交 证明无事务
                                  验证:NOT_SUPPORTED让当前事务挂起,非事务运行。即便是抛异常,只要被抓中,不影响事务恢复后的提交。
             
             
             
             【六】RROPAGATION_REQUIRES_NEW--->如果当前存在事务则挂起当前事务,并开启一个全新的事务。新事务与已存在的事务之间彼此没有关系。
                                             如果当前不存在事务,自己建立事务,运行。
                                             由此我们可以知道,对于REQUIRES_NEW事务传播机制,如果被调用端抛出运行时异常,则被调用端事务回滚
                                             如果调用段代码捕获了被调用端抛出的运行时异常,那么调用端事务提交,不回滚
                                             如果调用端未捕获被调用端抛出的运行时异常,那么调用端事务回滚,不提交
                                             
                                             当前事务和新事务没有关系。
                                             当前事务回滚,新事务可提交。
                                             新事务异常(回滚),不抓。当前事务回滚。
                                             新事物异常(回滚),抓住。当前事务无异常。可提交。
             
                     示例(1):评论博客。博主积分加50,博文加50,评论添加
                     步骤:--------commentService.saveCommentByBlogId()方法propagation=Propagation.REQUIRED
                             (1)给博主加50分authorService.addMoney(50, authorId);propagation=Propagation.REQUIRED
                             (2)给博客加一条评论commentService.saveComment(comment);propagation=Propagation.REQUIRES_NEW
                             (3)给博文加50分blogService.addmoneyBlogById(50, blogId);propagation=Propagation.REQUIRED
                     结果:(1)如果(1)(2)(3)皆顺利执行完毕---事务全部提交。
                                 (1)(3)融合进当前事务,在执行(2)时当前事务被挂起,(2)自己创建新事务,直到(2)执行完毕事务提交。挂起的事务恢复。提交当前事务。
                                 
                          (2)如果(1)顺利执行,(2)内部抛异常,但被外部抓住异常(3)顺利进行 --(1)(3)提交(2)回滚
                                      如果调用段代码捕获了被调用端抛出的运行时异常,那么调用端事务提交,不回滚
                                      
                          (3)如果(1)顺利执行,(2)内部抛异常,没有被外部抓住 (3)不执行---(1)回滚(2)回滚(3)未提交 。
                                      如果调用端未捕获被调用端抛出的运行时异常,那么调用端事务回滚,不提交
                                      
                          (4)如果(1)顺利执行,(2)顺利执行,(3)抛出异常---(1)回滚(2)提交(3)回滚
                                      当新建事务成功提交,但被挂起事务恢复后抛出异常,则不影响新建事务结果。除新建事务外,其他操作回滚。
                         
                     示例(2):给博客添加一条评论
                     步骤--------commentService.saveComment2()方法Propagation.REQUIRES_NEW
                             (1)控制器直接调用该方法
                     结果:(1)如果(1)顺利执行。评论提交
                          (2)如果(1)与数据库交互后,报错.数据回滚。说明如果当前运行环境不存在事务,自己会建立一个事务。
             
             【七】PROPAGATION_NESTED--->在当前事务上开启一个子事务(Savepoint),如果递交主事务。那么连同子事务一同递交。如果递交子事务则保存点之前的所有事务都会被递交。
                                        如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
                                        
                                        联合成功 --->父事务提交,子事务才算提交。【父事务影响子事务。子事务有选择影响父事务。若异常抓住,不影响父事务。】
                                        隔离失败--->父事务能执行完毕,子事务异常,数据回滚。父事务中除子事务外,其他都提交
                                        
                    示例(1):转账,转账前记录日志,转账后记录日志
                    步骤:------authorService.oToomoney(Integer outId,Integer inId,Integer money)propagation=Propagation.REQUIRED
                                (1)记录转账开始日志recordService.addRecord(record);Propagation.REQUIRED
                                (2)转账,出钱,减钱。authorService.oneTooneMoney(outId, inId, money)propagation=Propagation.NESTED
                                (3)记录转账结束日志recordService.addRecord(record);Propagation.REQUIRED
                    结果:(1)如果(1)(2)(3)顺利执行--(1)提交(2)提交(3)提交
                         (2)如果(1)(2)顺利(3)异常  --(1)回滚(2)回滚(3)回滚 联合成功
                         (3)如果(1)顺利(2)异常(3)未执行--(1)回滚(2)回滚(3)回滚
                         (4)如果(1)顺利(2)异常,但被抓住(3)顺利--(1)提交(2)回滚(3)提交 隔离失败

        【八】组合事务测试
                      示例(1):给一篇博客添加评论,并博客加分,博客作者加分。
                      步骤:----commentService.saveComent3(),无事务配置
                              (1)博客添加评论proxySelf.saveComent4(comment, blogId);Propagation.REQUIRED
                              (2)博客加积分blogService.addBlogAddMoney2(blogId, money);无事务配置
                              (3)博客作者加分authorService.addMoney2(money, authorId);Propagation.MANDATORY
                      结果:(1) 如果(1)(2)顺利执行(3)因为事务传播机制,报错No existing transaction found for transaction marked with propagation 'mandatory'
                                  (1)数据库操作提交(2)无事务运行,数据提交(3)异常,不影响(1)(2)的提交
                           (2)如果(1)顺利执行(2)交互后,抛出异常(3)因为事务传播机制,报错。
                                   (1)数据库操作提交(2)数据提交(3)未执行。执行也抛异常
                           (3)如果(1)顺利执行(2)交互前,抛出异常(3)因为事务传播机制,报错。
                                    (1)数据操作提交 (2)数据为提交。(3)因为事务传播机制,报错
                                   
                          (4)如果(1)抛异常(2)未执行(3)未执行
                                  (1)数据回滚(2)未执行(3)未执行
                          (5)如果(1)抛异常,但被抓住(2)执行(3)因事务传播机制,抛出异常
                                  (2)数据回滚(2)执行,提交数据(3)因事务传播机制抛异常,未执行

           示例(2):给一篇博客添加评论,并博客加分,博客作者加分。
                        步骤:----commentService.saveComent3(),无事务配置
                                (1)博客添加评论proxySelf.saveComent4(comment, blogId);Propagation.REQUIRED
                                (2)博客加积分blogService.addBlogAddMoney2(blogId, money);无事务配置
                                (3)博客作者加分authorService.addMoney2(money, authorId);Propagation.NESTED
                       结果:(1)如果(1)(2)(3)顺利执行完毕
                                   -->(1)数据提交(2)数据提交(3)数据提交
                                 
                              (2)如果(1)无论交互前或后抛异常,未抓(2)未执行(3)未执行
                                      -->(1)数据未提交(2)未执行(3)未执行
                              (3)如果(1)无论交互前和后抛异常,被抓(2)执行(3)执行
                                      -->(1)数据未提交(2)数据提交(3)数据提交
                         
                              (4)如果(1)顺利执行(2)交互后抛异常,未抓(3)未执行
                                      -->(1)数据提交(2)无事务运行,数据提交(3)数据未提交
                              (5)如果(1)顺利执行(2)交互前抛异常,未抓(3)未执行
                                      -->(1)数据提交(2)无事务运行,未交互为提交(3)数据未提交
                              (6)如果(1)顺利执行(2)交互前抛异常,被抓(3)执行
                                      -->(1)数据提交(2)无事务运行,未交互未提交(3)数据提交
                              (7)如果(1)顺利执行(2)交互后抛异常,被抓(3)顺利执行
                                      -->(1)数据提交(2)数据提交(3)数据提交
                         
                            (8)如果(1)顺利执行(2)顺利执行(3)无论交互前或后,抛异常,未抓
                                    -->(1)数据提交(2)无事务运行,数据提交(3)有事务运行,未提交
                            (9)如果(1)顺利执行(2)顺利执行(3)无论交互前或后,抛异常,被抓
                                    -->(1)数据提交(2)无事务运行,数据提交(3)有事务运行,未提交


             【九】组合事务测试
                     示例(1):给某篇博客添加50分积分,并在记录表中添加记录
                     步骤:---commentService.saveMoneyAndRecri()Propagation.REQUIRED
                             (1)添加积分blogService.addBlogAddMoney2(blogId, money) 无事务配置
                             (2)添加记录recordService.addRecord(record);Propagation.REQUIRED
                     结果:(1)如果(1)(2)顺利执行
                             (1)(2)数据库操作都提交
                          (2)如果(1)(2)顺利执行后,抛出异常
                              (1)(2)数据皆回滚。符合外部事务特性。
                         
                          (3)如果(1)执行(2)抛出异常
                              (1)(2)数据皆回滚。符合外部事务特性
                          (4)如果(1)执行(2)抛出异常,被抓住  
                              (1)(2)数据皆回滚。当前事务被标记,回滚。
                              整个业务方法执行完后,尽管业务方法内required业务方法所抛异常被抓获,但整个业务方法的连接已被标记为回滚。抛出如下异常
                              org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
                         
                          (5)如果(1)抛异常(2)未执行
                              (1)无论是与数据库交互前抛异常,还是交互后抛异常。(1)的数据都未提交,(2)未执行。
                          (6)如果(1)与数据库交互后,抛异常,被抓住(2)执行
                               (1)数据提交(2)数据提交
                          (7)如果(1)与数据库交互前,抛异常,被抓住(2)执行
                              (1)数据未提交(2)数据提交
                              
                           非事务方法运行,抛异常,不抓,但会影响当前事务的提交或回滚。是因为,非事务方法抛异常时,未安全退出当前事务的方法。致使当前事务的标示改变成回滚。
                       非事务方法运行,抛异常,但抓,则不会影响当前事务的提交或回滚。是因为,非事务方法抛异常后,被当前运行环境抓住,并且安全退出当前方法。所以事务提交。
                           安全退出---从一个方法}退出。
                        

  • 相关阅读:
    分层图最短路(DP思想) BZOJ2662 [BeiJing wc2012]冻结
    动态规划 BZOJ1925 地精部落
    线性DP SPOJ Mobile Service
    线性DP codevs2185 最长公共上升子序列
    数位DP POJ3208 Apocalypse Someday
    线性DP POJ3666 Making the Grade
    杨氏矩阵 线性DP? POJ2279 Mr.Young's Picture Permutations
    tarjan强连通分量 洛谷P1262 间谍网络
    树链剖分 BZOJ3589 动态树
    二分图 BZOJ4554 [Tjoi2016&Heoi2016]游戏
  • 原文地址:https://www.cnblogs.com/shangxiaofei/p/4171330.html
Copyright © 2011-2022 走看看