Java-Spring事务

事务

  • 什么是事务?

一系列操作要么全部完成,要么全部失败.

  • 事务的特新(ACID)?

    1. 原子性:事务最小的执行单位,不可分割的,要么全部完成,要么都不起作用.
    2. 一致性:事务执行前后,数据保持一致.
    3. 隔离性:并发时,一个事务不能影响另一个事务的执行.
    4. 持久性:事务一旦提交就是永久的,哪怕数据库故障也不会有影响.
    

Spring中的事务

程序是否支持事务取决于数据库,以Mysql为例,如果是myisam引擎的话,是不支持事务的.在Spring中事务有2种,编程式事务,声明式事务.

  • 编程式事务.

使用TransactionTemplate编写事务代码,当捕捉到异常时,执行回滚操作.

transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                try {
                    // ....  业务代码
                } catch (Exception e){
                    //回滚
                    transactionStatus.setRollbackOnly();
                }
            }
        });
  • 声明式事务.

推荐使用声明式事务,代码侵入低. @Transactional注解,基于AOP实现.

@Transactional(propagation=propagation.PROPAGATION_REQUIRED)
public void aMethod {
  //do something
  B b = new B();
  C c = new C();
  b.bMethod();
  c.cMethod();
}
  • 事务传播行为?

当多个事务存在时,如何处理事务.

  1. TransactionDefinition.PROPAGATION_REQUIRED

  默认值,当前存在事务就加入该事务,不存在事务就新建事务.
  注意:aMethod和bMethod只要有一个回滚,整个事务均回滚.即外部事务被PROPAGATION_REQUIRED修饰时,内部所有也被PROPAGATION_REQUIRED修饰的事务都是一个事务.
//代码
Class A {
    @Transactional(propagation=propagation.PROPAGATION_REQUIRED)
    public void aMethod {
        //do something
        B b = new B();
        b.bMethod();
    }
}

Class B {
    @Transactional(propagation=propagation.PROPAGATION_REQUIRED)
    public void bMethod {
       //do something
    }
}
  2. TransactionDefinition.PROPAGATION_REQUIRES_NEW

  无论当前是否存在事务,被其修饰的方法都会开启新的事务,且事务互相独立.
  注意:如下,使其修饰bMethod,aMethod不变;若aMethod异常回滚,bMethod不受影响.但是bMethod回滚,aMethod也会回滚.
//代码
Class A {
    @Transactional(propagation=propagation.PROPAGATION_REQUIRED)
    public void aMethod {
        //do something
        B b = new B();
        b.bMethod();
    }
}

Class B {
    @Transactional(propagation=propagation.REQUIRES_NEW)
    public void bMethod {
       //do something
    }
}
  3. TransactionDefinition.PROPAGATION_NESTED

  如果外部没有事务,那么内部事务新建事务且互相独立互不影响.
  如果外部有事务,那么内部事务属于外部事务的子事务,外部事务回滚,子事务回滚;子事务回滚不会影响外部事务.
  注意:如果 aMethod() 回滚的话,bMethod()和bMethod2()都要回滚,而bMethod()回滚的话,并不会造成 aMethod() 和bMethod()回滚。
//代码
Class A {
    @Transactional(propagation=propagation.PROPAGATION_REQUIRED)
    public void aMethod {
        //do something
        B b = new B();
        b.bMethod();
        b.bMethod2();
    }
}

Class B {
    @Transactional(propagation=propagation.PROPAGATION_NESTED)
    public void bMethod {
       //do something
    }
    @Transactional(propagation=propagation.PROPAGATION_NESTED)
    public void bMethod2 {
       //do something
    }
}
  4. TransactionDefinition.PROPAGATION_MANDATORY

  如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。 基本不用.
  • 以下配置发生错误也不会回滚

    1. TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
    2. TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
    3. TransactionDefinition.PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。
    
  • 事务超时属性?

    所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒,默认值为-1。
    
  • 事务只读属性?

    对于只有查询操作的事务,可以设置为readonly,即只读事务,一般用于多条数据库查询.
    
  • 为什么只读还要加事务?

    以MySQL的innodb为例: 
    
    MySQL 默认对每一个新建立的连接都启用了autocommit模式。在该模式下,每一个发送到 MySQL 服务器的sql语句都会在一个单独的事务中进行处理,执行结束后会自动提交事务,并开启一个新的事务。
    
  1. 加Transactional:

    所有的查询sql会放到一个事务中.

  2. 不加Transactional:

    每条sql会开启一个单独的事务,中间被其它事务改了数据,都会实时读取到最新值。

  • 什么时候使用只读事务?
  1. 如果你一次执行单条查询语句,则没有必要启用事务支持,数据库默认支持 SQL 执行期间的读一致性.

  2. 如果你一次执行多条查询语句,例如统计查询,报表查询,在这种场景下,多条查询 SQL 必须保证整体的读一致性,否则,在两条SQL查询之间,数据被其他用户改变,则该次整体的统计查询将会出现读数据不一致的状态.

  • 事务回滚规则?

默认回滚 RuntimeException的子类,也可以自己指定

@Transactional(rollbackFor= MyException.class)
  • @Transactional 注解原理
如果一个类或者一个类中的 public 方法上被标注@Transactional 注解的话,Spring 容器就会在启动的时候
为其创建一个代理类,在调用被@Transactional 注解的 public 方法的时候,实际调用的是,
TransactionInterceptor 类中的 invoke()方法。这个方法的作用就是在目标方法之前开启事务,方法执行过程中如果遇到异常的时候回滚事务,方法调用完成之后提交事务。
  • @Transactional 注解失效?
  1. @Transactional 注解只有作用到 public 方法上事务才生效,不推荐在接口上使用;
  2. 避免同一个类中调用 @Transactional 注解的方法,这样会导致事务失效;
  3. 正确的设置 @Transactional 的 rollbackFor 和 propagation 属性,否则事务可能会回滚失败

转自JavaGuide面试突击版

posted @ 2020-05-12 23:07  一千年以后1412  阅读(214)  评论(0编辑  收藏  举报