5 - 声明式事务

传统事务流程:

Connection connection = JdbcUtils.getConnection();
try {
    //1. 先设置事务不要自动提交
    connection.setAutoCommit(false);
    //2. 进行各种 crud
    //多个表的修改,添加 ,删除
    select from 商品表 => 获取价格
    //修改用户余额
    update ...
    //修改库存量
    update
    //3. 提交
    connection.commit();
} catch (Exception e) {
    //4. 回滚
    conection.rollback();
}

使用 Spring 的声明式事务处理, 可以将上面三个子步骤分别写成一个方法,然后统一管理

1. 使用实例

1.1 创建 src\tx_ioc.xml 文件





    
    
    
    



    
    



    




1.2 修改对象

修改 GoodsService.java,加入声明式事务注解

@Transactional
public void buyGoodsByTx(int user_id, int goods_id, int num) {
    //查询到商品价格
    Float goods_price = goodsDao.queryPriceById(goods_id);
    //购买商品,减去余额
    goodsDao.updateBalance(user_id, goods_price * num);
    //模拟一个异常, 会发生数据库数据不一致现象
    // int i = 10 / 0;
    //更新库存
    goodsDao.updateAmount(goods_id, num);
}

1.3 声明式事务机制

  • 使用@Transactional 可以进行声明式事务控制;即将标识的方法中的,对数据库的操作作为一个事务管理
  • @Transactional 底层使用的仍然是AOP机制
  • 底层是使用动态代理对象来调用buyGoodsByTx

执行流程:

  1. 在执行buyGoodsByTx() 方法 先调用 事务管理器的 doBegin(),主要是将自动提交关掉  
  2. 然后调用 buyGoodsByTx()
  3. 如果执行没有发生异常,则调用 事务管理器的 doCommit()
  4. 如果发生异常 调用事务管理器的 doRollback()

2. 事务的传播机制

当有多个事务处理并存时,如何控制?

2.1 事务的传播机制种类

  • REQUIRED:如果现在有事务在运行,当前的方法就是在这个事务内运行
  • REQUIRED_NEW:当前方法必须新启动一个事务,在自己的事务内运行
  • SUPPORTS:如果现在有事务在运行,当前方法就在这个事务内运行,否则它可以不运行在事务中
  • NOT_SUPPORTE:当前方法不应该运行在事务内,如果有事务,该方法将挂起
  • MANDATORY:当前方法必须运行在事务内,如果没有运行在事务,则抛出异常
  • NEVER:当前方法不可以运行在事务内,如果运行在事务,则抛出异常
  • NESTED:如果现在有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则就启动一个新的事务,并运行在自己的事务里

重点分析 REQUIRED 和 REQUIRED_NEW

1)REQUIED(默认事务传播机制)

5 - 声明式事务_第1张图片

2)REQUIRES_NEW 

5 - 声明式事务_第2张图片

 3)事务的传播机制的设置方法

 4)例子

5 - 声明式事务_第3张图片

如果设置为 REQUIRES_NEW:

buyGoods2 如果错误,不会影响到 buyGoods(),即它们的事务是独立的

如果设置为 REQUIRED:

buyGoods2 和 buyGoods 是一个整体,只要有方法的事务错误,那么两个方法都不会执行成功.!


3.  事务的隔离级别

这个概念参考 MySQL

5 - 声明式事务_第4张图片

3.1 说明

默认的隔离级别, 就是 mysql 数据库默认的隔离级别 一般为 REPEATABLE_READ

查看数据库默认的隔离级别:

SELECT @@global.tx_isolation

3.2 事务隔离级别的设置及测试

1)默认

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void buyGoodsByTxISOLATION(int user_id, int goods_id, int num) {
    //查询到商品价格
    Float goods_price = goodsDao.queryPriceById(goods_id);
    System.out.println("第一次读取的价格 =" + goods_price);
    //测试一下隔离级别,在同一个事务中,查询一下价格
    goods_price = goodsDao.queryPriceById(goods_id);
    System.out.println("第二次读取的价格 =" + goods_price);
}

过程:在读完第一次数据后,通过 mysql 进行修改该数据

结果:两次读取到的价格是一样的,不会受到 MySQL 修改影响

2)READ_COMMITTED 隔离级别情况

语法:

5 - 声明式事务_第5张图片

 结果:两次读取到的价格是不一样的,数据是会受到 MySQL 修改影响


4. 事务的超时回滚

目的:如果一个事务执行的时间超过某个时间限制,就让该事务回滚

语法:

5 - 声明式事务_第6张图片

超出时间会抛出异常,事务回滚,原来的操作撤销 

注:

  • timeout = 2 表示 buyGoodsByTxTimeout 如果执行时间超过了2秒, 该事务就进行回滚
  • 如果没有设置 timeout, 默认是 -1,表示使用事务的默认超时时间,或者不支持

你可能感兴趣的:(Spring5,学习笔记,java,数据库)