spring之事务概述

news2025/2/28 10:59:00

文章目录

  • 前言
  • 一、事务概述
    • 1、什么是事务
    • 2、事务的四个处理过程
    • 3、事务的四个特性
  • 二、引入事务场景
    • 1、引入依赖
    • 2、数据库创建
    • 3、建包
    • 4、spring.xml配置文件
    • 5、测试程序
    • 6、运行结果(成功)
    • 7、模拟异常
  • 三、Spring对事务的支持
    • 1、Spring实现事务的两种方式
    • 2、Spring事务管理API
    • 3、解决上述代码没有事务的问题
      • 3.1配置spring.xml
      • 3.2使用事务
  • 四、事务之传播行为
  • 五、事务之隔离级别


前言

1、什么是事务
2、事务的四个处理过程
3、事务的四个特性
4、事务使用场景
5、spring实现事务的两种方式
6、事务的传播行为
7、事务的隔离级别

一、事务概述

1、什么是事务

  • 在一个业务流程当中,通常需要多条DML(insert delete update)语句共同联合才能完成,这多条DML语句必须同时成果,或者同时失败,这样才能保证数据的安全。
  • 多条DML要么同时成功,要么同时失败,这叫做事务。
  • 事务:Transaction

2、事务的四个处理过程

  • 第一步:开启事务 start transaction
  • 第二步:执行核心业务代码
  • 第三步:提交事务(如果核心业务处理过程中没有出现异常)commit transaction
  • 第四步:回滚事务(如果核心业务处理过程中出现异常)rollback transaction

3、事务的四个特性

  • 原子性:事务是最小的工作单元,不可再分
  • 一致性:事务要求要么同时成功要么同时失败。
  • 隔离性:事务和事务之间因为有隔离性,才能保证互不干扰
  • 持久性:持久性是事务结束的标志

二、引入事务场景

以银行账户转账为例学习事务。两个账户act-001和act-002,act-001账户向act-002账户转账10000,必须同时成功,或者同时失败。(一个减成功,一个加成功,这两个update语句必须同时成功或失败)

1、引入依赖

1、spring-context依赖
2、spring-jdbc
3、mysql驱动 mysql-connector-java
4、德鲁伊连接池 druid
5、@Resource注解 lombok
6、单元测试

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.32</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.0.20</version>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.10</version>
    </dependency>
  </dependencies>

2、数据库创建

在这里插入图片描述
在这里插入图片描述

3、建包

com.powernode.bank.dao

AccountDao 专门负责账户信息的CRUD(增删改查)操作
DAO中只执行SQL语句,没有任何业务逻辑。
也就是说DAO不和业务挂钩

package com.powernode.bank.dao;

import com.powernode.bank.pojo.Account;

public interface AccountDao {
    /**
     * 根据账号查询账户信息
     * @param actno
     * @return
     */
    Account selectByActno(String actno);

    /**
     * 更新账户信息
     * @param act
     * @return
     */
    int update(Account act);
}

com.powernode.bank.dao.impl

AccountDaoImpl

package com.powernode.bank.dao.impl;

import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.pojo.Account;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;

@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
    @Resource(name="jdbcTemplate")
    private JdbcTemplate jdbcTemplate;

    @Override
    public Account selectByActno(String actno) {
        String sql = "select actno,balance from t_act where actno = ?";
        Account account = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Account.class), actno);
        return account;
    }

    @Override
    public int update(Account act) {
        String sql = "update t_act set balance = ? where actno = ?";
        int count = jdbcTemplate.update(sql, act.getBalance(), act.getActno());
        return count;
    }
}

com.powernode.bank.pojo

Account

package com.powernode.bank.pojo;

public class Account {
    private String actno;
    private double balance;

    public Account(String actno, double balance) {
        this.actno = actno;
        this.balance = balance;
    }

    public String getActno() {
        return actno;
    }

    public double getBalance() {
        return balance;
    }

    public Account() {
    }

    public void setActno(String actno) {
        this.actno = actno;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    @Override
    public String toString() {
        return "Account{" +
                "actno='" + actno + '\'' +
                ", balance=" + balance +
                '}';
    }
}

com.powernode.bank.service

AccountService 业务接口
事务就是在这个接口下控制的

package com.powernode.bank.service;

/**
 * 业务接口
 * 事务就是在这个接口下控制的
 */
public interface AccountService {

    /**
     * 转账业务方法
     * @param fromActno 从这个账户转出
     * @param toActno 转入这个账户
     * @param money 转账金额
     */
    void transfer(String fromActno,String toActno,double money);
}

com.powernode.bank.service.impl

AccountServiceImpl

package com.powernode.bank.service.impl;

import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.pojo.Account;
import com.powernode.bank.service.AccountService;

import javax.annotation.Resource;
@Repository("accountService")
public class AccountServicecImpl implements AccountService {

    @Resource(name="accountDao")
    private AccountDao accountDao;

    //控制事务,因为在这个方法中要完成所有的转账业务
    @Override
    public void transfer(String fromActno, String toActno, double money) {
        //查询转出账户的余额是否充足
        Account fromAct = accountDao.selectByActno(fromActno);
        if(fromAct.getBalance() < money){
            throw new RuntimeException("余额不足!!!");
        }
        //余额充足:
        Account toAct = accountDao.selectByActno(toActno);

        //将内存中两个对象的余额先修改
        fromAct.setBalance(fromAct.getBalance()-money);
        toAct.setBalance(toAct.getBalance()+money);

        //数据库更新
        int update = accountDao.update(fromAct);
        update += accountDao.update(toAct);

        if(update!=2){
            throw new RuntimeException("转账失败,联系银行");
        }

    }
}

4、spring.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.powernode.bank"></context:component-scan>

    <!--配置数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/dududu"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>
    <!--配置JDBCTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
</beans>

5、测试程序

package com.powernode;

import com.powernode.bank.service.AccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTxTest {
    @Test
    public void testSpringTx(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
        AccountService accountService = (AccountService) ac.getBean("accountService");
        try {
            accountService.transfer("act_001","act_002",10000);
            System.out.println("转账成功");
        }catch (Exception e){
            e.printStackTrace();
            System.out.println("转账失败");
        }
    }
}

6、运行结果(成功)

在这里插入图片描述
在这里插入图片描述

7、模拟异常

在两条update语句之间模拟异常

 //数据库更新
        int update = accountDao.update(fromAct);

        //模拟异常
        String s = null;
        s.toString();

        update += accountDao.update(toAct);

当前数据库中的数据:

在这里插入图片描述
执行测试程序【5】:

在这里插入图片描述
在这里插入图片描述
丢了一万块钱

事务管理:

@Repository("accountService")
public class AccountServicecImpl implements AccountService {

    @Resource(name="accountDao")
    private AccountDao accountDao;

    //控制事务,因为在这个方法中要完成所有的转账业务
    @Override
    public void transfer(String fromActno, String toActno, double money) {
//1、开启事务


//2、执行核心代码
        //查询转出账户的余额是否充足
        Account fromAct = accountDao.selectByActno(fromActno);
        if(fromAct.getBalance() < money){
            throw new RuntimeException("余额不足!!!");
        }
        //余额充足:
        Account toAct = accountDao.selectByActno(toActno);

        //将内存中两个对象的余额先修改
        fromAct.setBalance(fromAct.getBalance()-money);
        toAct.setBalance(toAct.getBalance()+money);

        //数据库更新
        int update = accountDao.update(fromAct);
        update += accountDao.update(toAct);

        if(update!=2){
            throw new RuntimeException("转账失败,联系银行");
        }
//3、如果执行业务流程过程中,没有异常提交事务

//4、如果执行业务流程过程中,遇到异常则回滚事务
    }
}

三、Spring对事务的支持

1、Spring实现事务的两种方式

  • 编程式事务:通过编写代码的方式来实现事务的管理
  • 声明式事务:1、基于注解方式 2、基于XML配置方式

2、Spring事务管理API

spring 对事务的管理底层实现方式是基于AOP实现的。采用AOP的方式进行了封装。所以spring专门针对事务开发了一套API,核心接口如下:
在这里插入图片描述
PlatformTransactionManager接口:spring事务管理器核心接口:

DataSourceTransactionManager:支持JdbcTemplate、Mybatis、Hibernate等事务管理
JtaTransaction
如果要在spring中使用J都不错Template,就要使用DataSourceTransactionManager来管理事务

3、解决上述代码没有事务的问题

spring声明式事务之使用注解方式:

3.1配置spring.xml

1、配置事务管理器
管理事务需要将connection.setAutocommit(false)–>需要Connection对象—>需要连接对象—>需要数据源

    <!--配置事务管理器-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

2、添加命名空间

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

3、开启事务注解驱动器
告诉spring框架,采用注解的方式去控制事务

<!--事务注解驱动器-->
    <tx:annotation-driven transaction-manager="txManager"></tx:annotation-driven>

3.2使用事务

在业务实现类上/方法 添加注解@Transactional即可

写在类上:代表这个类中所有的方法都应用事务
写在方法上:只是针对这个方法应用事务

在这里插入图片描述
重新执行测试程序(空指针异常):
在这里插入图片描述
数据库中的数据(一分钱没丢):
在这里插入图片描述
假设没有异常,删掉空指针异常的代码,运行结果:
在这里插入图片描述
数据库中的数据:

在这里插入图片描述

四、事务之传播行为

@Transactional的属性:
在这里插入图片描述
较为重要的:传播行为(propagation)、隔离级别(isolation)

什么是事务的传播行为?
在service类中有a()方法和b()方法,a()方法上有事务,b()方法上也有事务,当a()方法执行过程中调用了b()方法,事务是如何传递的?

事务传播行为在spring框架中被定义为枚举类型
在这里插入图片描述

七种传播行为

1、REQUIRED:支持当前事务,如果不存在就新建一个【没有就新建,有就加入】
2、SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行【有就加入,没有就不管了】
3、MANDATORY:必须运行在一个事务中,如果当前没有事务发生,将抛出一个异常【有就加入,没有就抛出异常】
4、REQUIRES_NEW:开启一个新的事物,如果一个事务已经存在,则将这个事务挂起【不管有没有,直接开启一个新事务,开启的新事务和之前的事务不存在嵌套关系,之前事务被挂起】
5、NOT_SUPPORTED:以非事务方式运行,如果有事务存在,挂起当前事务【不支持事务,存在就挂起】
6、NEVER:以非事务方式运行,如果事务存在,抛出异常【不支持事务,存在就抛异常】
7、NESTED:如果当前正有一个事务在进行中,则该方法应当运行在一个嵌套式事务中。被嵌套的事务可以独立于外层事务进行提交或回滚。如果外层事务不存在,行为就像REQUIRED一样【有事务的话,就在这个事务里嵌套一个完全独立的事务,嵌套的事务可以独立的提交和回滚。没有事务就和REQUIRED一样】

不设置的话默认是REQUIRED

五、事务之隔离级别

事务隔离级别类似于教室A和教室B之间的一道墙,隔离级别越高表示墙体越厚,隔音效果越好。

数据库中读取数据存在的三大问题:(三大读问题)【重要】

  • 脏读:读取到没有提交到数据库中的数据,叫做脏读
  • 不可重复读:在同一个事务当中,第一次和第二次读取的数据不一样
  • 幻读:读到的数据是假的

事务隔离级别包括四个级别:【重要】
在这里插入图片描述

  • 读未提交:READ_UNCOMMITTED

这种隔离级别,存在脏读问题

  • 读提交:READ_COMMITTED

解决了脏读问题,其他事务提交之后才能读到,但存在不可重复读问题

  • 可重复读:REPEATABLE_READ

解决了不可重复读,可以达到可重复读效果,只要当前事务不结束,读取到的数据一直都是一样的。但存在幻读问题

  • 序列化:SERIALIZABLE

解决了幻读问题,事务排队执行。不支持并发

在这里插入图片描述

如果是Oracle数据库 默认是READ_COMMITTED
如果是MySQl数据库 默认是REPEATABLE_READ


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/361227.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数值方法笔记4:插值、近似和拟合

1. 插值1.1 插值的一些概念1.1.1 插值的定义1.1.2 插值的存在性1.1.3 插值的误差分析1.2 拉格朗日插值(Lagrange Interpolation)1.2.1 拉格朗日插值误差分析1.3 Newton多项式插值1.3.1 Newton多项式插值误差分析1.4 Chebyshev多项式确定插值点1.4.1 Chebyshev多项式性质1.5 有理…

Green Hills Software(GHS)的安装

Green Hills Software(GHS)简介 Green Hills Software(GHS)是美国Green Hills软件公司提供的一种具有调试、编译器和闪存编程工具的集成开发环境,是汽车电子行业常用且重要的开发环境之一。它支持的功能包括:AUTOSAR感知、项目构建器、代码覆盖、运行时错误检查、MISRA C…

【HEC-RAS水动力】HEC-RAS 1D基本原理(恒定流及非恒定流)

一、数据说明 HEC-RAS模型主要由工程文件 (.prj) 文 件 、 河道地形数据文件 ( .g01)、运行文件(p01)、非恒定流文件 ( .u01) 等部分组成。 1. 一般数据 在创建并保存project文件(*.prj)后,其他data文件均会自动以同样的名字保存,但采用不同的后缀来区分各类文件。 &qu…

网络编程之IP 地址的介绍

IP 地址的介绍学习目标能够说出IP 地址的作用1. IP 地址的概念IP 地址就是标识网络中设备的一个地址&#xff0c;好比现实生活中的家庭地址。网络中的设备效果图:2. IP 地址的表现形式说明:IP 地址分为两类&#xff1a; IPv4 和 IPv6IPv4 是目前使用的ip地址IPv6 是未来使用的i…

Redis进阶-缓存问题

Redis 最常用的一个场景就是作为缓存&#xff0c;本文主要探讨Redis作为缓存&#xff0c;在实践中可能会有哪些问题&#xff1f;比如一致性、击穿、穿透、雪崩、污染等。 为什么要理解Redis缓存问题 在高并发业务场景下&#xff0c;数据库大多数情况都是用户并发访问最薄弱的…

计算机是怎么读懂C语言的?

文章目录前言程序环境翻译环境翻译环境分类编译预处理预处理符号预定义符号#define#undef命令行定义条件编译文件包含头文件包含查找规则嵌套文件包含其他预处理指令编译阶段汇编链接&#x1f389;welcome&#x1f389; ✒️博主介绍&#xff1a;博主大一智能制造在读&#xff…

Linux 目录操作命令

目录操作命令 进入目录 cd命令 cd 目录名 进入指定目录 cd / 进入根目录 cd … 返回上级目录 cd - 返回上一次所在的目录 cd ~ 进入当前用户的家目录 cd /usr 进入指定目录&#xff0c;绝对路径 创建目录 mkdir 目录名 在usr目录下创建dd目录 进入目录 cd 目录名 进入us…

TypeScript 学习笔记

最近在学 ts 顺便记录一下自己的学习进度&#xff0c;以及一些知识点的记录&#xff0c;可能不会太详细&#xff0c;主要是用来巩固和复习的&#xff0c;会持续更新 前言 想法 首先我自己想说一下自己在学ts之前&#xff0c;对ts的一个想法和印象&#xff0c;在我学习之前&a…

性能测试知多少?怎样开展性能测试

看到好多新手&#xff0c;在性能需求模糊的情况下&#xff0c;随便找一个性能测试工具&#xff0c;然后就开始进行性能测试了&#xff0c;在这种情况下得到的性能测试结果很难体现系统真实的能力&#xff0c;或者可能与系统真实的性能相距甚远。 与功能测试相比&#xff0c;性能…

适合打游戏用的蓝牙耳机有哪些?吃鸡无延迟的蓝牙耳机推荐

现在手游的兴起&#xff0c;让游戏市场变得更加火爆&#xff0c;各种可以提高玩家体验的外设也越来越多&#xff0c;除了提升操作的外置按键与手柄外&#xff0c;能带来更出色音质与舒心使用的游戏耳机&#xff0c;整体氛围感更好&#xff0c;让玩家在细节上占据优势&#xff0…

2023年2月中国数据库排行榜:OTO新格局持续三月,人大金仓、AnalyticDB排名创新高

玉兔迎春至&#xff0c;榜单焕新颜。 2023年2月&#xff0c;兔年开年的 墨天轮中国数据库流行度排行 火热出炉&#xff0c;本月共有259个数据库参与排名&#xff0c;排行榜样式去掉了较上月和半年前得分差的数据显示&#xff0c;更加聚焦各产品之间的排名变化以及当月的得分差距…

景区展馆客流量数据如何统计

客流统计系统是了解掌握景区客流量的小助手&#xff0c;只有知道每天景区来往客流的情况&#xff0c;才能对经营管理、员工分配等多方面要素进行合理布局安排。使用客流统计系统&#xff0c;利用大数据管理预测&#xff0c;以客流数据为基础&#xff0c;提高景区对突发事件的应…

【云原生】kubeadm部署k8s集群、Dashboard UI以及连接私有仓库

一、kubeadm 部署 K8S 集群架构 主机名IP地址安装组件master&#xff08;2C/4G&#xff0c;cpu核心数要求大于2&#xff09;192.168.2.66docker、kubeadm、kubelet、kubectl、flannelnode01&#xff08;2C/2G&#xff09;192.168.2.200docker、kubeadm、kubelet、kubectl、fla…

基本bash shell(浏览、管理、查看文件/目录)

基本bash shell使用shell启动shell$cat /etc/passwd文件存储用户账号列表和基本配置信息shell提示符默认&#xff1a;$表示&#xff1a;shell在等待用户输入bash手册man命令$man man表示&#xff1a;查看手册页内容$man -K terminal关键字搜索。表示&#xff1a;查找与终端相关…

弄懂 Websocket 你得知道的这 3 点

1. WebSocket原理 WebSocket同HTTP一样也是应用层的协议&#xff0c;但是它是一种双向通信协议&#xff0c;是建立在TCP之上的。 WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket API也被W3C定为标准。 WebSocket使得客户端和服务器之间的数据交换变得更加简…

扬帆优配|联通港股创近两年新高!A股资源类股爆发,食品饮料领跌

今日上午&#xff0c;A股商场和港股商场均现较大起伏震动&#xff0c;临近上午收盘出现一波跳水&#xff0c;不过&#xff0c;到上午收盘&#xff0c;上证指数仍微涨0.10%&#xff0c;煤炭等资源类板块明显上涨。 港股商场上午走弱&#xff0c;科技股领跌。 沪指微涨0.10%资源…

进程间通信(二)/共享内存

⭐前言&#xff1a;在前面的博文中分析了什么的进程间通信和进程间通信的方式之一&#xff1a;管道&#xff08;匿名管道和命名管道&#xff09;。接下来分析第二种方式&#xff1a;共享内存。 要实现进程间通信&#xff0c;其前提是让不同进程之间看到同一份资源。所谓共享内存…

虹科直播 | 2月25日相约虹科Pico学院,教您CAN总线测试与波形诊断分析

​​2月25日 直播预告 网络总线的测试与波形诊断分析 2月18日 直播回顾 2月18日&#xff0c;虹科Pico汽车示波器学院成功开展第六课的直播课程。戈华飞老师向各位学员讲解了车身天线信号&#xff08;无钥匙进入系统&#xff09;、倒车雷达信号、车内超声波监控信号测试与波形…

操作系统真相还原_第6章:完善内核

文章目录6.1 函数调用约定简介6.2 汇编语言和C语言混合编程汇编调用CC调用汇编6.3 实现打印函数流程程序编译并写入硬盘执行6.4 内联汇编简介汇编语言AT&T语法基本内联汇编扩展内联汇编6.1 函数调用约定简介 调用约定&#xff1a; calling conventions 调用函数时的一套约…

OpenCV基础(28)使用OpenCV进行摄像机标定Python和C++

摄像头是机器人、监控、太空探索、社交媒体、工业自动化甚至娱乐业等多个领域不可或缺的一部分。 对于许多应用&#xff0c;必须了解相机的参数才能有效地将其用作视觉传感器。 在这篇文章中&#xff0c;您将了解相机校准所涉及的步骤及其意义。 我们还共享 C 和 Python 代码以…