Spring(四)-声明式事务

Spring-04 声明式事务

1、事务的定义

事务就是由 一组逻辑上紧密关联多个工作单元(数据库操作)而合并成一个整体,这些操作 要么都执行,要么都不执行

2、事务的特性:ACID

1)原子性A : 原子即不可再分,表现:一个事务涉及的多个操作在业务逻辑上缺一不可,保证 同一个事务中的操作要不 都提交,要不 都不提交

2)一致性C :数据的一致性,一个事务中,不管涉及到多少个操作,都 必须保证数据提交的正确性(一致);即:如果在 事务数据处理中, 有一个或者几个操作失败,必须 回退所有的数据操作,恢复到事务处理之前的 统一状态;

3)隔离性I :程序运行过程中,事务是 并发执行的,要求每个 事务之间都是隔离的,互不干扰;

4)持久性D :事务 处理结束,要将数据进行 持久操作,即永久保存。

3、事务的分类:

1)编程式事务-使用jdbc原生的事务处理,可以将事务处理写在业务逻辑代码中,违背aop原则,不推荐;

2)声明式事务-使用事务 注解 @Transactional,可以 声明在方法上,也可以 声明在类上

*

 **优先级**:
 *      <mark>&#x58F0;&#x660E;&#x5728;**&#x7C7B;&#x4E0A;**&#xFF0C;&#x4F1A;&#x5BF9;**&#x5F53;&#x524D;&#x7C7B;&#x5185;&#x7684;&#x6240;&#x6709;&#x65B9;&#x5F0F;&#x751F;&#x6548;**&#xFF08;&#x6240;&#x6709;&#x65B9;&#x6CD5;&#x90FD;&#x6709;&#x4E8B;&#x52A1;&#x5904;&#x7406;&#xFF09;;</mark>
 *      <mark>&#x58F0;&#x660E;&#x5728;**&#x65B9;&#x6CD5;&#x4E0A;**&#xFF0C;&#x53EA;&#x4F1A;**&#x5BF9;&#x5F53;&#x524D;&#x65B9;&#x6CD5;&#x751F;&#x6548;**&#xFF0C;&#x5F53;&#x7C7B;&#x4E0A;&#x548C;&#x65B9;&#x6CD5;&#x4E0A;&#x540C;&#x65F6;&#x5B58;&#x5728;&#xFF0C;**&#x65B9;&#x6CD5;&#x7684;&#x4F18;&#x5148;&#x7EA7;&#x9AD8;&#x4E8E;&#x7C7B;**&#xFF08;&#x6709;&#x4E9B;&#x65B9;&#x6CD5;&#xFF0C;&#x5BF9;&#x58F0;&#x660E;&#x5F0F;&#x4E8B;&#x52A1;&#x505A;&#x7279;&#x6B8A;&#x5C5E;&#x6027;&#x914D;&#x7F6E;&#xFF09;;</mark>

4、事务的属性:

4.1 事务的传播行为:propagation属性

事务的传播行为:propagation 属性指定;

一个带事务的方法另一个带事务的方法调用时(事务嵌套),当前事务如何处理:

  • propagation = Propagation. REQUIRED :
  • 默认值, 使用调用者的事务(全程就一个事务,如果有 事务嵌套,以 外部事务为主);
  • propagation = Propagation. REQUIRES_NEW :
  • 调用者事务直接挂起,自己 重开新的事务处理,结束提交事务,失败回滚;(当 事务嵌套时,内层事务,会 重新开启新事务的处理不受外部事务的管理);

4.2 事务的隔离级别:isolation属性

事务的隔离级别:isolation属性指定隔离级别,只有InnoDB支持事务,所有这里说的事务隔离级别指的是InnoDB下的事务隔离级别。

1、读未提交 : 读取其它事务未提交的数据,了解,基本不会使用;

2、读已提交 : oracle的默认事务隔离级别同一个事务处理中, 只能读取其它事务提交后的数据(也就是说事务提交之前对其余事务不可见);

3、可重复读 : mysql默认事务隔离级别同一个事务处理中, 多次读取同一数据是都是一样的,不受其它事务影响;

4、串行化 : 可以 避免上面 所有并发问题,但是执 行效率最低数据一致性最高

4.3 事务的指定回滚和不会滚

事务的指定回滚和不会滚:Spring在 默认的情况下,是对所有的运行时异常会 执行事务回滚

1、 rollbackFor : 指定回滚异常, 只有产生了指定的异常类型,才会回滚事务;

2、 noRollbackFor : 指定不会滚异常, 产生了指定的异常类型,也不会回滚事务;

4.4 事务的超时时长-了解

1、timeout,指定事务出现异常,没有及时回滚,单位是秒,防止事务超时,占用资源;

4.5 事务的只读-了解

1、readOnly=false,默认,可读可写’;

2、readOnly=true, 代表该事务处理,理论上只允许读取,不能修改(只是通知spring,并不是一个强制选项)
目的就是:提示数据库驱动程序和数据库系统,这个事务并不包含更改数据的操作,那么JDBC驱动程序和数据库就有可能根据这种情况对该事务进行一些特定的优化,比方说不安排相应的数据库锁,以减轻事务对数据库的压力,毕竟事务也是要消耗数据库的资源的。
但是你非要在”只读事务”里面修改数据,也并非不可以,只不过对于数据一致性的保护不像”读写事务”那样保险而已。

5、 环境搭建

5.1主要 jar包

4.3.18.RELEASE
5.1.47
0.9.5.2

    org.springframework
    spring-jdbc
    ${spring.version}

    org.springframework
    spring-orm
    ${spring.version}

    com.mchange
    c3p0
    ${c3p0.version}

    org.springframework
    spring-tx
    ${spring.version}

    mysql
    mysql-connector-java
    ${mysql.version}

5.2 配置文件


6、测试

5.1 购买一辆车(没有事务嵌套)

TES 购买一辆 AudiQ5;

模拟购买一辆车,主要流程:(1,2,3 整体是一个事务)

1、据买家购买汽车编号,获取汽车详情;

2、扣汽车的库存

3、扣买家的余额

5.1.2 主要业务代码

5.1.2.1 扣用户余额业务

BuyerServiceImpl

如果买家余额不足,直接返回;

@Service
public class BuyerServiceImpl implements BuyerService {

    @Autowired
    private BuyerDao buyerDao;

    @Override
    public void subBuyerMoneyByName(String buyerName, Car car) {

        // 根据买家姓名,查询买家详情
        Buyer buyer = buyerDao.selectBuyerByName(buyerName);

        // 判断买家余额是否充足,如果不足,不能继续扣减金额
        if(buyer.getMoney() < car.getPrice()){
            System.out.println(String.format("****** 买家:%s,余额不足! ------", buyerName));
            return;  //直接return
        }

        // 余额充足,执行扣减余额
        int row = buyerDao.updateBuyerMoneyById(buyer.getId(), car.getPrice());

        System.out.println(String.format("****** 买家:%s,余额扣减成功,影响行数:%s ------", buyerName, row));

    }

}
5.1.2.2 扣库存业务

CarsStockServiceImpl

如果库存不足,直接返回;

@Service
public class CarsStockServiceImpl implements CarsStockService {

    @Autowired
    private CarsStockDao carsStockDao;

    @Override
    public void subCarsStockBuyId(Car car) {

        //根据汽车编号,插叙汽车详情
        CarsStock carsStock = carsStockDao.selectCarsStockByCid(car.getId());

        //判断库存是否充足,如果不足,不能购买
        if(carsStock.getStock()
5.1.2.3 用户买车业务

根据 卖家名字,和汽车编号买车;

BuyCarServiceImpl

@Service("buyCarService") //方便从容器中获取对象
public class BuyCarServiceImpl implements BuyCarService {

    @Autowired
    private CarDao carDao;

    @Autowired
    private CarsStockService carStockService;

    @Autowired
    private BuyerService buyerService;

    //根据 卖家名字,和汽车编号买车
    @Override
    public void buyCar(String buyerName, Integer carId) {

        System.out.println(String.format("------ 买家:%s,购买汽车编号:%s 开始 ------",buyerName,carId));

        // 根据买家购买汽车编号,获取汽车详情
        Car car = carDao.selectCarById(carId);

        // 扣买家的余额
        buyerService.subBuyerMoneyByName(buyerName, car);

        // 扣汽车的库存
        carStockService.subCarsStockBuyId(car);

        System.out.println(String.format("------ 买家:%s,购买汽车编号:%s 结束 ------",buyerName,carId));

    }

}

5.1.3 测试(没有添加事务处理)

5.1.3.1 测试前的数据
  • 汽车价格

Spring(四)-声明式事务
  • 用户余额

Spring(四)-声明式事务
  • 库存

Spring(四)-声明式事务

根据观察,发现用户TES的余额不够买AudiQ5;

5.1.3.2 测试
//没有添加事务处理
@Test
public void testSpringUnUsedTx(){
    //获取买车的业务实现对象
    BuyCarService buyCarService = context.getBean("buyCarService", BuyCarService.class);

    //调用买车的业务方法
    buyCarService.buyCar("TES",1);

}

&#x8FD0;&#x884C;&#x7ED3;&#x679C;&#xFF1A;

Spring(四)-声明式事务
5.1.3.3 测试后的数据
  • 用户余额

Spring(四)-声明式事务
  • 库存

Spring(四)-声明式事务

5.1.4 测试 (加上@Transactional 注解添加事务处理)

5.1.4.1 方法上加上@Transactional 注解
@Transactional
public void buyCar(String buyerName, Integer carId) {
   ...

}
5.1.4.1 测试

恢复初始数据后测试;

Spring(四)-声明式事务

5.1.5 测试 (增加异常抛出)

余额不足,没有异常 直接return, 不能触发事务

需要 抛出自定义异常才会 触发事务处理

5.1.5.1 自定义异常类

BuyCarException

public class BuyCarException extends RuntimeException {

    //生成所有的构造方法
    public BuyCarException() {
    }

    public BuyCarException(String message) {
        super(message);
    }

    public BuyCarException(String message, Throwable cause) {
        super(message, cause);
    }

    public BuyCarException(Throwable cause) {
        super(cause);
    }

    public BuyCarException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }

}
5.1.5.2 抛出异常

当余额或库存不足的时候,抛出自定义异常;

BuyerServiceImpl

@Service
public class BuyerServiceImpl implements BuyerService {

    @Autowired
    private BuyerDao buyerDao;

    @Override
    public void subBuyerMoneyByName(String buyerName, Car car) {

        Buyer buyer = buyerDao.selectBuyerByName(buyerName);

        if(buyer.getMoney() < car.getPrice()){
            System.out.println(String.format("****** 买家:%s,余额不足! ------", buyerName));
            //return; //没有异常直接return,不能触发事务
            //余额不足抛出自定义异常

            //*****余额充足,执行扣减余额*****
            throw  new BuyCarException(String.format("****** 买家:%s,余额不足! ------", buyerName));
        }

        int row = buyerDao.updateBuyerMoneyById(buyer.getId(), car.getPrice());

        System.out.println(String.format("****** 买家:%s,余额扣减成功,影响行数:%s ------", buyerName, row));

    }

}

CarsStockServiceImpl

@Service
public class CarsStockServiceImpl implements CarsStockService {

    @Autowired
    private CarsStockDao carsStockDao;

    @Override
    public void subCarsStockBuyId(Car car) {

        CarsStock carsStock = carsStockDao.selectCarsStockByCid(car.getId());

        if(carsStock.getStock()
5.1.5.3 测试

恢复初始数据后测试;

Spring(四)-声明式事务
5.1.5.4 测试 (余额充足)
5.1.5.4.1 测试前的数据
  • 用户余额

Spring(四)-声明式事务
  • 库存

Spring(四)-声明式事务
5.1.5.4.2测试

Spring(四)-声明式事务
5.1.5.4.3 测试后的数据
  • 用户余额

Spring(四)-声明式事务
  • 库存

Spring(四)-声明式事务

5.2购买两辆车(有事务嵌套) **对过程理解还有问题

JDG 购买一辆 AudiQ5 和一辆 BmwX3

模拟购物车一次购买两辆车,主要流程:(1,2 整体是一个事务)

1、买第一辆车(1.1,1.2,1.3 整体是一个事务 默认情况内部事务不生效)

​ 1.1 据买家购买汽车编号,获取汽车详情;

​ 1.2扣汽车的库存

​ 1.3扣买家的余额

2、买第二辆车(1.1,1.2,1.3 整体是一个事务 默认情况内部事务不生效)

​ 1.1 据买家购买汽车编号,获取汽车详情;

​ 1.2扣汽车的库存

​ 1.3扣买家的余额

5.2.1 主要业务代码

模拟购物车一次购买两辆车;

多次调用购买一辆汽车业务;

BuyCarCartServiceImpl

@Service("BuyCarCartService" )
public class BuyCarCartServiceImpl implements BuyCarCartService {

    @Autowired
    private BuyCarService buyCarService;

    @Override
    @Transactional //购物车外层事务注解,buyCarService接口方法中也有事务注解
    public void buyCarCart(String buyerName, List carIds) {
        //模拟购物车垢面多辆车,方便演示事务传播行为,一辆一辆购买(单独调用买车接口)
//        carIds.forEach(carId -> buyCarService.buyCar(buyerName,carId));

        for (int i = 0; i < carIds.size(); i++) {
            //异常处理,防止买第一辆车出现异常后,无法购买第二辆车
            try{
                buyCarService.buyCar(buyerName,carIds.get(i));
            }catch (Exception e){
                e.printStackTrace();
            }
        }

    }
}

5.2.1 propagation = Propagation.REQUIRED

默认传播特性,以 外部事务 为主;propagation = Propagation.REQUIRED 可以不写;

5.2.1.1 测试前的数据
  • 汽车价格

Spring(四)-声明式事务
  • 用户余额

Spring(四)-声明式事务
  • 库存

Spring(四)-声明式事务
5.2.1.2测试
//测试事务存在 事务嵌套 的传播行为
//购物车结算
@Test
public void testSpring(){

    BuyCarCartService buyCarCartService = context.getBean("BuyCarCartService", BuyCarCartService.class);

    //调用购物车买车的业务方法
    buyCarCartService.buyCarCart("JDG", Arrays.asList(1,2));

}

&#x6D4B;&#x8BD5;&#x7ED3;&#x679C;&#xFF1A;

Spring(四)-声明式事务
5.2.1.3测试后的数据
  • 用户余额

Spring(四)-声明式事务
  • 库存

Spring(四)-声明式事务
5.2.1.4 总结

通过 查看数据库的数据发现,数据 没有改变,说明事务并 事务已经回滚,也就是说 默认传播特性,以 外部事务 为主

5.2.3 propagation = Propagation.REQUIRES_NEW

propagation = Propagation.REQUIRES_NEW的传播特性, 内部事务会自己 重开新的事务处理;

5.2.3.1 内部事务注解添加属性参数

buyCar&#x65B9;&#x6CD5;

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void buyCar(String buyerName, Integer carId) {
    ...

}
5.2.3.2 测试

恢复数据再测试;

Spring(四)-声明式事务
5.2.3.3测试后的数据
  • 用户余额

Spring(四)-声明式事务
  • 库存

Spring(四)-声明式事务
5.2.3.4 总结

通过 查看数据库的数据发现,数据 发生改变,说明 内部事务重新开起 新的事务处理不受外部事务的管理

Original: https://www.cnblogs.com/xiaoqigui/p/16647592.html
Author: 化羽羽
Title: Spring(四)-声明式事务

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/616432/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Java复制Word文档

    Microsoft Word 提供了许多易于使用的文档操作工具,同时也提供了丰富的功能集供创建复杂的文档使用。在使用的时候,你可能需要复制一个文档里面的内容到另一个文档。本文介绍使…

    Java 2023年6月15日
    082
  • 这个简单的小功能,半年为我们产研团队省下213个小时

    大多数人对产研同学的认知都是每天做着高大上的活儿。 我们以为的产研团队是: 研发负责人:今年最新的技术架构是什么、我的团队适合吗? 开发同学:010001,一顿代码猛如虎 测试同学…

    Java 2023年6月8日
    087
  • 设计模式-代理模式

    概念:二十三种设计模式中的一种,属于结构型模式。它的作用就是通过提供一个代理类,让我们在调用目标 方法的时候,不再是直接对目标方法进行调用,而是通过代理类 间接调用。让不属于目标方…

    Java 2023年6月16日
    057
  • 一致性hash原理 看这一篇就够了

    ​ 在了解一致性哈希算法之前,最好先了解一下缓存中的一个应用场景,了解了这个应用场景之后,再来理解一致性哈希算法,就容易多了,也更能体现出一致性哈希算法的优点,那么,我们先来描述一…

    Java 2023年6月7日
    081
  • Java获取本机IP

    import lombok.extern.slf4j.Slf4j; import org.junit.Test; import java.net.Inet4Address; imp…

    Java 2023年5月29日
    089
  • 回溯法实现全排序Ⅰ

    给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 &#x793A;&#x4F8B; 1&#xFF1A; &…

    Java 2023年6月16日
    089
  • 程序员你如何检查参数的合法性?

    作为程序员的你,代码中最多的就是各种方法了,你是如何对参数进行校验的呢? 背景 大部分的方法和构造函数对传入的参数值有一些限制,比如:常见的索引值必须是非负数,对象引用不能为空。 …

    Java 2023年6月8日
    090
  • java学习之JSP

    JSP:全拼写:java Server pages:java 服务器端页面可以理解为一个特殊的页面:可以定义html代码也可以定义java的代码定义:JSP是简化Servlet编写…

    Java 2023年6月13日
    072
  • nacos单机,集群安装部署

    nacos单机启动 准备 下载nacos安装包 准备centos环境 (本次测试使用docker) PS C:\Users\Administrator> docker run…

    Java 2023年6月8日
    088
  • IO操作

    1、有5个学生,每个学生有三门课的成绩,从键盘输入以上数据(学生号,姓名,三门课的成绩),计算出平均成绩,将上述数据存放在文件”stud”中 import …

    Java 2023年6月6日
    083
  • java selenium (六) XPath 定位

    xpath 的定位方法, 非常强大。 使用这种方法几乎可以定位到页面上的任意元素。 什么是xpath xpath 是XML Path的简称, 由于HTML文档本身就是一个标准的XM…

    Java 2023年5月29日
    077
  • 解决Mapper.xml文件中sql标签第一个字段报错

    在文件标头的http后边补上www 下边代码仅第4行有变动 原文件: 修改后的文件: Original: https://www.cnblogs.com/yang9/p/16145…

    Java 2023年6月5日
    081
  • [Java]HashMap与ConcurrentHashMap的一些总结

    HashMap底层数据结构 JDK7:数组+链表 JDK8:数组+链表+红黑树 JDK8中的HashMap什么时候将链表转为红黑树? 当发现链表中的元素大于8之后,判断当前数组长度…

    Java 2023年6月5日
    076
  • FreeMarker 去掉循环末尾的符号

    在使用 FreeMarker 模板引擎来生成文件时,经常会使用到 list 标签用于循环生成。 有时会遇到需要处理末尾符号的情况,比如 Json 文件,循环生成的标签中末尾是不需要…

    Java 2023年6月6日
    079
  • SQL查询语句–统计

    — 1、日统计查询填补 i->为时间差的天数 2022-05-10为终止时间 SET @i :=- 1; SELECT date_format( DATE_SUB( ’20…

    Java 2023年6月5日
    083
  • JAVA漏洞扫描工具之墨菲安全for IDEA

    背景 最近log4j漏洞猖狂,某天一个好心网友提交了一份分析报告,指出开源软有问题墨菲漏洞扫描报告forSpringBootCodeGenerator,当时马上就更新所有依赖到最新…

    Java 2023年5月29日
    096
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球