通过基于注解的声明式事务实现事务功能~

2023-05-16

编程式事务:

事务功能的相关操作全部通过自己编写代码来实现

Connection conn=.........;
try{
	//开启事务;关闭事务的自动提交
	conn.setAutoCommit(false);
	//核心操作
	
	//提交事务
	conn.commit;
	}
	catch(Exception e){
	//回滚事务
	conn.rollback();
}finally{
	//释放数据库连接
	conn.close();
}

上述这种编程式的实现方式存在很多缺陷:

细节没有被屏蔽,具体操作过程中,所有细节都需要程序员自己来完成,比较繁琐
代码复用性不高:如果没有有效抽取出来,每次实现功能都需要自己编写代码,代码就没有得到复用

声明式事务:

既然事务控制的代码有规律可循代码的结构基本是确定的,所以框架就可以将固定模式的代码抽取出来,进行相关的封装

封装起来后,我们只需要在配置文件中进行简单的配置即可完成操作

这样做能够提高开发效率消除了冗余的代码,框架会综合考虑相关领域中在实际开发环境下有可能遇到的各种问题,进行了健壮性性能等各个方面的优化

基于注解的声明式事务:实现事务功能

数据库准备工作:

创建图书表:

<!-- comment是作为列或者行的注释的-->
create table t_book(book_id int not null auto_increment comment '主键',book_name varchar(20) default null comment '图书名称',price int default null comment '价格',stock int unsigned default null comment '库存(无符号)',primary key(book_id));

插入数据:

insert into t_book(book_id,book_name,price,stock)values(1,'斗破苍穹',80,100),(2,'斗罗大陆',50,100);

创建用户表:

create table t_user(user_id int not null auto_increment comment '主键',username varchar(20) default null comment '用户名',balance int unsigned default null comment '余额(无符号)',primary key(user_id));

插入数据:

insert into t_user(user_id,username,balance) values (1,'admin',50);

通过生活经验可知,图书购买的过程往往会出现余额不足或者库存不足的情况,它就类似于我们在进行事务处理时产生的异常,而需要执行回滚操作,但我们早在学习mysql中就说过,引起事务回滚的原因通常并不是SQL语句出现错误,而是业务的核心逻辑出现问题,但是业务逻辑并不会产生异常,那么要处理出现问题的业务,我们可以在数据库层面对其进行解决,也可以在java代码中对其进行处理,在数据库中进行的处理即为,库存量和余额量不能小于0,为这两个字段设置关键字unsigned,设置当前字段的值是不能为负值的,当然也可以通过在java代码层面自定义异常等,当业务逻辑出现问题时,抛出异常即可

java准备工作:

数据库连接的部分写在外部的jdbc.properties文件中:

driver=com.mysql.cj.jdbc.Driver 
url=jdbc:mysql://localhost:3306/WJR
name=root
password=你的密码

TXannotation.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/context
          https://www.springframework.org/schema/context/spring-context.xsd
          http://www.springframework.org/schema/beans
          https://www.springframework.org/schema/beans/spring-beans.xsd">
    <context:component-scan base-package="spring_txAnnotation"></context:component-scan>
    
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${driver}"></property>
        <property name="url" value="${url}"></property>
        <property name="username" value="${name}"></property>
        <property name="password" value="${password}"></property>
    </bean>
    
  <!-- 配置JdbcTemplate bean对象-->
    <bean class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
</beans>

通过创建我们之前学习的经典的三层架构模型,实现业务逻辑:

控制层:

package spring_txAnnotation.Controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import spring_txAnnotation.Service.ServiceBook;

@Controller
public class BookController {
    @Autowired
    private ServiceBook serviceBook;
    public  void  BuyBook(Integer userId,Integer BookId){
        serviceBook.buyBook(userId,BookId);
    }
}

持久层:

package spring_txAnnotation.Dao;

public interface BookDao {
    Integer getPrice(Integer bookId);//根据图书的id查询图书的价格
    void updateStock(Integer bookId);//根据图书的id查询图书的库存量
    void updateBalance(Integer userId, Integer price);
}

持久层的实现类:

package spring_txAnnotation.Dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class BookDaoImpl implements BookDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Override
    public Integer getPrice(Integer bookId) {
        String sql="select price from t_book where book_id=?";
        return jdbcTemplate.queryForObject(sql,Integer.class,bookId);
    }

    @Override
    public void updateStock(Integer bookId) {
        String sql="update t_book set stock=stock-1 where book_id=?";
        jdbcTemplate.update(sql,bookId);

    }
    
    @Override
    public void updateBalance(Integer userId,Integer price) {
        String sql="update t_user set balance=balance-? where user_id=?";
        jdbcTemplate.update(sql,price,userId);
    }
}

业务层:

package spring_txAnnotation.Service;

public interface ServiceBook  {
    void buyBook(Integer userId, Integer bookId);
}

业务层的实现类:

package spring_txAnnotation.Service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import spring_txAnnotation.Dao.BookDao;

@Service
public class ServiceBookImpl implements ServiceBook {
    @Autowired
    private BookDao bookDao;
    @Override
    public void buyBook(Integer userId, Integer bookId) {
        //查询图书的价格
      Integer book_price=bookDao.getPrice(bookId);
        //更新图书的库存
        bookDao.updateStock(bookId);
        //更新用户的余额
        bookDao.updateBalance(userId,book_price);
    }
}

测试类:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import spring_txAnnotation.Controller.BookController;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:TXannotation.xml")
public class Test1 {
    @Autowired
    private BookController bookController;
    @Test
    public void testBuyBooks(){
        bookController.BuyBook(1,1);
    }
}

运行后,报错如下:
在这里插入图片描述

org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [update t_user set balance=balance-? where user_id=?]; Data truncation: BIGINT UNSIGNED value is out of range in '(`wjr`.`t_user`.`balance` - 80)'; nested exception is com.mysql.cj.jdbc.exceptions.MysqlDataTruncation: Data truncation: BIGINT UNSIGNED value is out of range in '(`wjr`.`t_user`.`balance` - 80)'

原因如下:属性为无符号的这个字段的值超出了范围

Caused by: com.mysql.cj.jdbc.exceptions.MysqlDataTruncation: Data truncation: BIGINT UNSIGNED value is out of range in '(`wjr`.`t_user`.`balance` - 80)'

上述的SQL语句对数据实现的是,用当前余额的50元去购买价格为80的图书编号为1的图书,二者相减后的结果为负数,但因为我们将其类型设置为无符号的类型,因此就产生错误

在之前学习mysql中,我们就讲到mysql默认是有提交事务的功能,一个SQL语句对应一个事务提交,那么自动提交事务的功能会带来什么麻烦呢?

如下所示:

下述的这三个方法各自对应单独的事务,也就是说,他们事务提交成功与否只和自身有关,与其他操作并没有关系

在这里插入图片描述

首先我们先来查看一下数据库中的数据情况,如下所示:

在这里插入图片描述

当我们发生了使用不足的余额去购买图书这个操作后,java控制台报错如上述,数据库中的数据变化,如下所示:

在这里插入图片描述

我们发现用户的余额并没有任何的改变,但是1号图书的库存量减少了1,那么也证实了我们上述所说的,不同的操作之间是互不影响的,但这种结果,显然不符合逻辑,下面我们就通过基于注解的声明式事务来对这种情况进行处理

上面在学习声明式事务的概念时,我们提到过,声明式事务不需要我们单独的写切面和通知,只需要在配置文件中进行简单的配置即可完成操作

第一步:在XML文件中,配置事务管理器-->处理数据必须有数据源,如下所示:

由于事务管理器是一个接口,如果想将接口设置为bean,则必须通过其实现类来完成

在这里插入图片描述

<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
        <!--引用数据源-->
        <property name="dataSource" ref="dataSource"></property>
</bean>

第二步:在XML文件中,开启事务的注解驱动–>驱动并不是环绕通知,事务管理器才是,我们通过注解驱动中的transaction-manager属性将其二者关联起来 ,而属性值使用为默认值

<tx:annotation-driven transaction-manager="transactionManager"/>

注意:这里的annotation有多个,一定要使用tx的

在这里插入图片描述

第三步:将其注解使用在方法上

使用@Transactional注解所标识的方法或类中所有的方法使用事务进行管理,也就是说@Transactional作用于那个方法上,那个方法就是连接点,如果加到类上,则该类上的所有方法都是连接点,transaction-manager用来设置事务管理器的id,如果该id是默认值[transactionManager],可以省略不写

如下所示:将其添加至方法上

在这里插入图片描述

报错如下:

org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [update t_user set balance=balance-? where user_id=?]; Data truncation: BIGINT UNSIGNED value is out of range in '(`wjr`.`t_user`.`balance` - 80)'; nested exception is com.mysql.cj.jdbc.exceptions.MysqlDataTruncation: Data truncation: BIGINT UNSIGNED value is out of range in '(`wjr`.`t_user`.`balance` - 80)'
Caused by: com.mysql.cj.jdbc.exceptions.MysqlDataTruncation: Data truncation: BIGINT UNSIGNED value is out of range in '(`wjr`.`t_user`.`balance` - 80)'

咿?不禁让人产生疑惑,怎么进行事务管理之后的报错内容和上述没有进行管理时完全相同啊,好像事务管理的作用并没有在这里体现,那么我们再去查看数据库部分,是否也和上面完全相同呢?

如下所示:

与上述未进行事务管理不同的地方在于,这里的1号图书的数量并没有减少,用户余额同样也是,因为余额不足的情况下,购买失败,因此整个事务进行了回滚,显然这种才是符合逻辑的

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

通过基于注解的声明式事务实现事务功能~ 的相关文章

  • 数据结构之C语言单链表操作

    实验目的 xff1a 1 xff0e 创建一个带头结点的单链表 2 xff0e 插入元素操作 xff1a 将新元素x插入到单链表head的头部 将新元素x插入到单链表head的尾部 将新元素x插入到单链表head中第i个元素之后 3 xff
  • DBUS入门与C编程

    https blog csdn net weixin 45566765 article details 125028296 一 D Bus简介 1 D Bus是什么 D Bus最主要的用途是在 Linux 桌面环境为进程提供通信 xff0c
  • 模拟IIC——关于模拟IIC的IO口的配置选取推挽输出还是开漏输出,以及是否需要更改IO口输入输出模式和是否需要对IO配置上拉

    在使用模拟IIC的时候 xff0c 观看别人的程序的时候发现了程序之间的一些不一样的地方 代码1 IO方向设置 define SDA IN GPIOB gt MODER amp 61 3 lt lt 9 2 GPIOB gt MODER 6
  • C语言——链表

    C语言 链表 链表是一种基础的数据结构类型 xff0c 一种能够动态维护数据的线性数据表 链表的数据以结点形式存储信息 xff0c 并通过结点之间的指针实现结点之间的衔接 为什么要用链表 xff1f 链表和数组类似 xff0c 但是功能比数
  • 学习记录 | ZigBee协议栈工作流程

    第一次来CSDN记录一下学习过程 xff0c 其实就是笔记啦 之前用Typora 的 但前几天电脑出问题重装系统后打开笔记文件发现照片都打不开了 xff0c 索性想换一种记笔记方式 好啦 以下是正文 xff01 xff01 xff01 对了
  • 学习记录 | ZigBee协议栈实践——串口收发数据

    上次的理论知识学的有点杂乱 今天来跟着例程实践看一看 目录 一 ZigBee协议栈的安装 编译和下载 二 协议栈工作流程 三 串口通信主要代码 1 串口打印 2 串口打印收到的数据 四 实现 五 总结 一 ZigBee协议栈的安装 编译和下
  • 第一次画异形板后的总结感悟

    目录 画原理图前的准备 画PCB的一些好方法 蜂鸣器电路 供电电路 其他电路 杂七杂八的随记要点 总结 像这样的异形板是通过solidworks扫描实物生成的 画原理图前的准备 当然是选购元器件 一般习惯在嘉立创进行选购 选择符合要求 有库
  • 自学物联网ESP第一天

    先简单的使用串口通信AT指令 刚开始不知道用什么开发好 在博客找了很久本来打算用 eclipse 不过突然发现可以用Arduino 不过下载真的好慢 于是开始看一下函数准备一下 一 基本函数 1 setup 函数 Arduino控制器通电或
  • stm32串口发送+接收

    本文章转自B站江科大自化协 一发送 接线图 目标结构 Serial c include 34 stm32f10x h 34 include lt stdio h gt 1移植printf函数 封装sprintf include lt std
  • vscode配置C/C++环境(超详细保姆级教学)

    大一上学期被学长安利了vscode xff0c 但是下载安装后不会配置 xff0c 自己连查带问搞了七八个小时终于配置好了 后面身边很多同学也不会配 xff0c 都来找我帮忙配 xff0c 加上之前自己摸索着配的时候感觉网上没有详细又靠谱的
  • 路由器接口解析

    ensp 接口解析 路由器的CON和AUX con是串口 xff0c 接口电脑串口可以进路由器的命令行查看和设置 一般为设备的控制端口 xff0c Console端口使用配置专用连线直接连接至计算机的串口 xff0c 利用终端仿真程序进行路
  • 解决VSCode写html文件时<!+Enter导入模板快捷键没办法使用的问题

    好久没有写前端了 xff0c 先是用模板快捷键 lt 43 Enter导入模板 xff0c 发现没有办法使用 找到网上一些资料 xff0c VSCode使用 html 5 取代了之前的生成模板快捷键 示例 xff1a 在空白html文本里逐
  • LVGL 之 windows 上 lvgl 模拟器 基于 Visual Studio 搭建方法的简单整理

    mark GUI Guider与lvgl联合仿真 xff08 结合stm32实验进行演示 xff0c 含触摸屏实验计数器与计算器 xff09 https blog csdn net gudao07 article details 12752
  • 初认识stm32 ————每日小总结(串口通信初始化基础操作)

    留下代码 xff0c 日后复习 c文件 include 34 my uart h 34 void my uart init NVIC PriorityGroupConfig NVIC PriorityGroup 2 设置中断优先级分组 RC
  • Sqoop全量导入mysql表数据到HDFS

    我是在三个节点运行的 xff0c 主节点只有namenode和mysql数据库 1 开启服务 具有NameNode和DataNode start all sh 2 进入sqoop的目录下并且输入代码 下面的命令用于从 MySQL 数据库服务
  • 在字符串中查找子字符串

    查找子串 include lt stdio h gt char Search char str1 char str2 char s1 61 str1 char s2 61 str2 char cur 61 str1 while cur s1
  • 基于相机云台周期运动的“远帧差”图像处理算法

    传统的帧差法针对前后两帧或者某几帧进行图像处理 xff0c 一旦相机视角转动 xff0c 就不能获得很好的检测效果 xff0c 我所参与的项目就是一个云台搭载一台红外相机进行取像 xff0c 这样可以大幅度的节约成本 xff0c 但是对图像
  • 深究C语言4.链表和结构体

    目录 一 xff0c 结构体 一 xff0c 结构体的定义 二 xff0c 结构体的概念 1 2 3 4 5 6 三 xff0c 结构变量的使用 1 结构变量的操作符 gt 和 2 结构变量的赋值 3 结构变量作为函数参数 4 结构指针 5
  • 使用c++解析http\https数据遇到的问题(附代码)

    开发环境 xff1a vs2019 系统 xff1a win10 openssl版本 xff1a Win32OpenSSL 3 0 7 访问网页 xff1a www baidu com 先说明以下问题 xff1a c 43 43 在发送ht

随机推荐

  • vector的使用

    vector的介绍 1 vector是表示可变大小数组的序列容器 2 vector就像数组一样 xff0c 也采用的连续空间来存储元素 xff0c 这也意味着可以采用下标对vector的元素进行访问 3 vector与普通数组不同的是 xf
  • Linux系统( Centos 7) 配置与管理Apache服务器实例详细步骤

    Linux系统 xff08 Centos 7 xff09 配置与管理Apache服务器实例详细步骤 服务器centos7 1 1 配置网络 root 64 centos7 vim etc sysconfig network scripts
  • 什么是Cmake和Makefile?

    目录 1 前言 2 什么是Makefile 3 什么是Cmake 4 Cmake的语法规则 1 前言 在学习视觉SLAM十四讲的时候 xff0c 对这个Cmake的概念不太清楚 xff08 笔者是机械方向转到SLAM这个方向的 xff09
  • 创建任务7.24

    创建任务 1 什么是任务 在裸机系统中 xff0c 系统的主体就是 main 函数里面顺序执行的无限循环 xff0c 这个无限循环里面 CPU 按照顺序完成各种事情 在多任务系统中 xff0c 我们根据功能的不同 xff0c 把整个系统分割
  • Linux驱动开发系列:DRM(第十部分)

    https www iotword com 7227 html 一 DRM简介 DRM xff0c 全称Direct Rending Manger 是目前Linux主流的图形显示框架 相比较传统的Framebuffer xff0c DRM更
  • 解决Ubuntu与Virtualbox虚拟机共享文件夹无权限的问题(保姆级)

    目录 使用这个命令后 xff0c 如果不能马上打开共享文件夹 xff0c 需要重启一下ubuntu系统 这样 xff0c 你共享的work文件夹就能在Ubuntu系统访问了 声明 CSDN 64 m0 637799558昵称更改为 64 孤
  • char类型与int类型的转换

    在c语言中 xff0c char类型与int类型可以转换 xff0c 如何转换我在此做一个粗略的总结 首先是char转换为int include lt stdio h gt int main char a a 61 110 int c 61
  • C语言中字符串大小与长度的区分

    字符串大小和字符串长度不同 xff1a 字符串大小指该字符串占用多少空间 字符串长度指该字符串的字符个数
  • 【字符串函数】strcat的使用及原理

    1 strcat函数的函数声明 char strcat char Destination const char Source 2 strcat函数的头文件 include lt string h gt 3 strcat函数的使用 strca
  • 【DRM】DRM Display Driver Guide

    https zhuanlan zhihu com p 534267979 目录 收起
  • TCP协议

    1 TCP协议的可靠传输 我们知道TCP协议的特点有 有连接 可靠传输 面向字节流 全双工 其中连接 面向字节流和全双工在 网络编程 该文章中详细说明了 而要深入了解TCP协议 了解可靠传输也是重中之重 可靠传输也是TCP协议的一大特点 那
  • c/c++程序运行不出结果?

    1 xff0c 条件语句未执行 在运行过程中 xff0c 用到if或while等条件语句 xff0c 条件之后的语句没有执行就结束了 xff0c 且输出变量也未初始化 xff0c 当然就输出不了任何东西了 xff1b 2 xff0c 变量未
  • 【解救ROS】关于ros机器人(小车)动态调试PID参数

    1 打开终端 xff0c 连接树莓派 ssh clbrobot 64 clbrobt 2 打开底盘节点 roslaunch clbrobot bringup launch 3 再打开一个终端 ssh clbrobot 64 clbrobt
  • 十进制转十六进制(C语言)

    首先要注意输入非负整数的范围 xff1a 这里用long表示 xff1b long 取值 2147483648 2147483647 include lt stdio h gt int main long n int i 61 0 j ch
  • C语言之数组的定义及其使用方法

    作者 xff1a 从未止步 博客主页 xff1a 从未止步的博客 专栏 xff1a 和我一起学C 语录 xff1a Every day is a second chance 行动是理想最高贵的表达 xff0c 给大家介绍一款超牛的斩获大厂o
  • python之常量的定义

    常量 xff1a 与C语言不同的是 xff0c python中并没有用来修饰常量的修饰符 xff0c 在python中可以通过自定义实现常量 xff0c 要求常量的标识符必须全是大写字母 xff0c 且该值不能被修改 举例 xff1a PI
  • C语言之根据摄氏温度求华氏温度

    求摄氏温度26 C对应的华氏温度 计算公式 xff1a f 61 9 c 5 43 32 xff0c 式中 xff1a c表示摄氏温度 xff0c f表示华氏温度 输入输出示例 xff1a celsius 61 26 fahr 61 78
  • python文件的操作和异常之异常

    异常 xff1a python使用称为异常的特殊类对象来管理程序执行期间发生的错误 xff0c 每当发生让python不知所措的错误时 xff0c 他都会创建一个异常的对象 如果你编写了处理该异常的代码 xff0c 程序将继续进行 xff0
  • Java---抽象类和接口

    抽象类 xff1a 抽象类的基本概念 xff1a 在面向对象的概念中 xff0c 所有的对象都是通过类来描述并创建的 xff0c 但是有一种特殊的类 xff0c 并不能用完整的信息来描述一个具体的对象 xff0c 这样的类就是抽象类 xff
  • 通过基于注解的声明式事务实现事务功能~

    编程式事务 xff1a 事务功能的相关操作全部通过自己编写代码来实现 xff1a span class token class name Connection span conn span class token operator 61 s