zoukankan      html  css  js  c++  java
  • 事务属性及事务分组

    一、Spring事务属性

      接上一节<<Spring的事务控制>>,Spring提供了@Transactional的注解来帮助控制事务,对于这个注解中涉及的几个属性需要说明和掌握一下

      @Transactional(isolation=Isolation.DEFAULT,rollbackFor=ArithmeticException.class,timeout = -1,readOnly=false,propagation=Propagation.REQUIRED)

      propagation传播行为:定义关于客户端和被调用方法的事物边界

      isolation隔离级别:并发访问下数据库的全安性

      timeout 事务超时:事务的最长持续时间,如果事务一直没有提交或回滚,那么超出该时间后,系统将自动回滚事务,单位秒。-1表示不超时,最终由底层数据库系统决定。

      readOnly只读状态:只读事务不修改任何数据,可以优化查询操作。

      rollbackFor和noRollbackFor:指定异常回滚或不回滚

      其中关于隔离级别的配置主要有以下几种:

      1. DEFAULT (默认) 
    这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.另外四个与JDBC的隔离级别相对应 
      2.READ_UNCOMMITTED (读未提交) 
    这是事务最低的隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。 
      3.READ_COMMITTED (读已提交) 
    保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。 
      4.REPEATABLE_READ (可重复读) 
    这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了不可重复读 
      5.SERIALIZABLE(串行化) 
    这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。 

      关于事务传播属性的配置主要有以下几种:

    传播行为

    意义

    REQUIRED

    业务方法需要在一个事务中运行。如果方法运行时,已经处在一个事务中,那么加入到该事务,否则为自己创建一个新的事务

    NOT_SUPPORTED

    声明方法不需要事务。如果方法没有关联到一个事务,容器不会为它开启事务。如果方法在一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行

    REQUIRESNEW

    属性表明不管是否存在事务,业务方法总会为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务会被挂起,新的事务会被创建,直到方法执行结束,新事务才算结束,原先的事务才会恢复执行

    MANDATORY

    该属性指定业务方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果业务方法在没有事务的环境下调用,容器就会抛出例外。

    SUPPORTS

    这一事务属性表明,如果业务方法在某个事务范围内被调用,则方法成为该事务的一部分。如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行

    Never

    指定业务方法绝对不能在事务范围内执行。如果业务方法在某个事务中执行,容器会抛出例外,只有业务方法没有关联到任何事务,才能正常执行

    NESTED

    如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按REQUIRED属性执行.它使用了一个单独的事务, 这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效

    二、事务传播属性案例

      接着上一节的代码,假设在业务实现类PersonService中引入多个DAO,并且都涉及到了增删改操作,在都添加事务控制的前提下,如何做到各个DAO之间的事务互不影响。

      如下代码:在业务方法operateMethod中引入logDao和personDao,如果logDao和personDao的操作都在一个事务中,那么operateMethod一旦出现异常,则两个Dao中的逻辑都会被回滚掉,如果两个Dao各自的事务传播属性都是REQUIRED,由于REQUIRED的定义是:业务方法需要在一个事务中运行。如果方法运行时,已经处在一个事务中,那么加入到该事务,否则为自己创建一个新的事务。则一旦operateMethod出现异常,两个Dao依然会被同时回滚。

      如果需要做到operateMethod发生异常时,logDao不回滚,而personDao回滚,则需要将logDao的事务传播属性设置为REQUIRES_NEW,如此以来,执行到operateMethod方法时,会开启一个事务A,再走到logDao时,原先的事务A会挂起,开启一个新的事务B,待到logDao执行完毕,新的事务B提交,原先A的事务恢复,继续往下执行。

    package com.jyk.spring.annotation.transaction.property;
    
    import javax.annotation.Resource;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Isolation;
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    
    /*
     * 在一个service中调用多个dao
     */
    @Service
    public class PersonService {
    
        @Resource
        private PersonDao personDao;
        
        @Resource
        private LogDao logDao;
        
        /*
         * @Transactional的使用位置
         * 1、定义到方法上:当前方法应用的spring的声明式事务
         * 2、定义到类上:当前类的所有方法都应用spring的声明式事务
         * 3、定义到父类上:当执行父类的方法的时候应用Spring的声明式事务
         */
        @Transactional(
                readOnly=false, //读写事务,只读事务不修改任何数据
                timeout = -1,//事务的超时时间没有限制
            //    noRollbackFor = ArithmeticException.class, //遇到数学异常不回滚
                isolation = Isolation.DEFAULT, //事务的隔离级别    
                propagation = Propagation.REQUIRED //传播行为
                )    
        public void operateMethod(Person person)
        {
            logDao.save();//打印日志<开启自己的事务>
            int myexception = 1/0;
            personDao.addPerson(person);//添加用户
        }
    }
    package com.jyk.spring.annotation.transaction.property;
    
    import javax.annotation.Resource;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.stereotype.Repository;
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    
    @Repository
    public class LogDao {
        
        @Resource
        private JdbcTemplate jdbcTemplate;
        
        //在事务中自己开启自己的事务
        @Transactional(
            propagation = Propagation.REQUIRES_NEW
        )
        public void save()
        {
            String sql = "insert into mylog(content) values('正在打印日志...')";
            jdbcTemplate.update(sql);
        }
    }

    三、事务分组

      事务分组这一概念被广泛地应用于各大企业应用中,用来控制长链条的业务逻辑中,将业务切分为各小模块,并且各小模块处于互补影响的事务中,从而实现整个链条发生异常时,部分业务回滚,而部分业务不回滚的场景。

      假设A->B->C->D这样一个调用场景,如果想实现整个链路异常时,只有C回滚,AB和D不回滚,A或B出现异常时AB回滚,D出现异常时D才会回滚,类似这种复杂的事务控制场景,需要如何实现?

      很简单,如果看了《Spring的事务控制》和本节的内容,只要熟练的使用REQUIRE和REQUIRES_NEW即可控制,我们可将整个链路ABCD放在一个execute方法中,execute开启REQUIRE事务1,到A时使用REQUIRES_NEW属性开启新的事务2,同时B也加入到该事务2中,C加入到事务1中,同时D使用REQUIRES_NEW属性开启新的事务3,就能实现了。

      思路如下:

      REQUIRE

      execute(){
      

      AB();  

      C();

      REQUIRES_NEW

      D();

      }

      REQUIRES_NEW

      AB(){  

      A();

      B();

      }

  • 相关阅读:
    Python (一)Tkinter窗口组件:Label
    Python (八)Tkinter窗口组件:Scrollbar
    Python (四)Tkinter窗口组件:Radiobutton
    Python (五)Tkinter窗口组件:LabelFrame
    Python (三)Tkinter窗口组件:Checkbutton
    Scrapy安装及相关知识点概括
    Python (九)Tkinter窗口组件:Scale
    Python (六)Tkinter窗口组件:Entry
    电脑通过蓝牙适配器连接手机与蓝牙耳机之经验
    Noi2018 归途
  • 原文地址:https://www.cnblogs.com/jiyukai/p/9387703.html
Copyright © 2011-2022 走看看