JDBC事务操作

news2024/11/15 19:44:10

1、 事务概述

事务的概念:逻辑上的一组SQL语句(一个或者多个)执行的时候要么全部执行成功,要么全部执行失败。

事务的案例:转账案例:一个账户中的余额减少,另一个账户中的余额增加。

更新数据:update set 余额=原有的余数 - 要转账的余额 where 转出账户的账户

更新语句:update set 余额=原有的余额+ 要转账的余额 where 转入账户的账户

要执行的SQL语句是两个update语句,要么都成功,要么都失败,不允许一个执行成功,另一个执行失败。

同一组SQL语句,就是用同一个数据库连接对象执行的!

  • 数据准备

-- 1:创建一个银行数据库
create database if not exists  bank;
​
-- 2: 打开数据库
use bank;
​
​
-- 3: 创建银行账户
create table if not exists  account(
    id int primary key auto_increment,
    name varchar(20),
    money double
);
​
-- 4:插入tom和jerry的账户表1000
insert into account(name, money) VALUES ('tom',2000),('jerry',2000);
​
​
-- 5:查询数据表
select * from account;
​
-- 6:修改数据表
update account set money=2000;

2、 事务的具体操作

  • 开启事务,或者称为阻止事务的自动提交

  • 提交事务,所有SQL语句都执行成功,提交事务,数据持久化

  • 回滚事务,如果有部分SQL语句执行失败,回滚事务,数据不会改变

数据库自身事务的操作命令:

命令命令的作用
begin开启事务
commit提交事务
rollback回滚事务

Java中的事务操作:核心是数据库连接对象Connection:

数据库连接对象的方法方法的作用
setAutoCommit(false)开启事务,阻止事务的自动提交
commit提交事务
rollback回滚事务

每个数据库连接对象都有自己的事务,针对当前的数据库连接对象是有效的!

一旦事务提交或者回滚,当前事务就结束了!

7.3 原始JDBC转账案例中的事务

需求:JDBC技术实现转账功能,tom账户向jerry账户转1000元。

①:添加Lib库中的jar包

②:druid.properties配置文件

driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/school
username=root
password=mysql密码
initialSize=10
maxActive=20

③:创建units包指的是DataSourceUtils

package com.rocky.utils;
​
import com.alibaba.druid.pool.DruidDataSourceFactory;
​
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
​
public class DataSourceUtils {
    static DataSource dataSource;
    static{
        InputStream resourceAsStream =
                DataSourceUtils.class.getClassLoader().getResourceAsStream("druid.properties");
​
        Properties properties=new Properties();
​
        try {
            properties.load(resourceAsStream);
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
​
    public static DataSource getDataSource(){
        return dataSource;
    }
​
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
​
    public static void close(ResultSet resultSet, Statement statement,Connection connection) throws SQLException {
​
        if(resultSet!=null){
            resultSet.close();
        }
​
        if(statement!=null){
            statement.close();
        }
​
        if(connection!=null){
            connection.close();
        }
​
    }
}
 

④:准备pojo中的javaBean

package com.rocky.pojo;
​
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
​
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account {
    
    private Integer id;
    private String name;
    private double money;
}
​

⑤:不使用事务操作

创建一个NoTransfer的事务类

package com.rocky.transfer;
import com.rocky.utils.DataSourceUtils;
​
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
​
/**
 * 实现转账功能
 * 功能的实现步骤
 * 1:定义需要的数据,收款人和付款人的姓名,转账金额
 * 2: 定义2个更新语句 update语句
 * 3:使用同一个数据库连接对象执行SQL语句
 * */
public class NoTransfer {
​
    public static void main(String[] args) throws SQLException {
        // 付款人的姓名tom
        String fromName="tom";
        // 收款人的姓名jerry
        String toName="jerry";
        //转账金额
        double money=500;
        //定义数据库操作需要的对象
        Connection connection=null;
        PreparedStatement preparedStatement=null;
​
        try {
            //从连接池中获取数据库连接
            connection= DataSourceUtils.getConnection();
​
            //获取SQL语句的执行对象
            // 减钱的SQL语句
            String sql1="update account set money=money-? where name=?";
            preparedStatement=connection.prepareStatement(sql1);
​
            preparedStatement.setObject(1,money);
            preparedStatement.setObject(2,fromName);
            //执行SQL语句
            int num1 = preparedStatement.executeUpdate();
​
​
​
//            // 增加钱的SQL语句
            String sql2="update account set money=money+? where name=?";
​
            preparedStatement=connection.prepareStatement(sql2);
​
            preparedStatement.setObject(1,money);
            preparedStatement.setObject(2,toName);
            //执行SQL语句
            int num2=preparedStatement.executeUpdate();
​
            //打印出结果
            System.out.println("num1="+num1+"num2="+num2);
​
​
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            DataSourceUtils.close(null,preparedStatement,connection);
        }
​
    }
}
​

执行成功:

中间人为的加一个错误

   //中间加一句话 模拟出现一个错误
            double a=1/0;
package com.rocky.transfer;
import com.rocky.utils.DataSourceUtils;
​
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
​
/**
 * 实现转账功能
 * 功能的实现步骤
 * 1:定义需要的数据,收款人和付款人的姓名,转账金额
 * 2: 定义2个更新语句 update语句
 * 3:使用同一个数据库连接对象执行SQL语句
 * */
public class NoTransfer {
​
    public static void main(String[] args) throws SQLException {
        // 付款人的姓名tom
        String fromName="tom";
        // 收款人的姓名jerry
        String toName="jerry";
        //转账金额
        double money=500;
        //定义数据库操作需要的对象
        Connection connection=null;
        PreparedStatement preparedStatement=null;
​
        try {
            //从连接池中获取数据库连接
            connection= DataSourceUtils.getConnection();
​
            //获取SQL语句的执行对象
            // 减钱的SQL语句
            String sql1="update account set money=money-? where name=?";
            preparedStatement=connection.prepareStatement(sql1);
​
            preparedStatement.setObject(1,money);
            preparedStatement.setObject(2,fromName);
            //执行SQL语句
            int num1 = preparedStatement.executeUpdate();
​
​
            //中间加一句话
            double a=1/0;
​
//            // 增加钱的SQL语句
            String sql2="update account set money=money+? where name=?";
​
            preparedStatement=connection.prepareStatement(sql2);
​
            preparedStatement.setObject(1,money);
            preparedStatement.setObject(2,toName);
            //执行SQL语句
            int num2=preparedStatement.executeUpdate();
​
            //打印出结果
            System.out.println("num1="+num1+"num2="+num2);
​
​
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            DataSourceUtils.close(null,preparedStatement,connection);
        }
​
    }
}
​

这就是没有事务操作的写法

⑥:使用事务的操作

package com.rocky.transfer;
import com.rocky.utils.DataSourceUtils;
​
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
​
/**
 * 实现转账功能
 * 功能的实现步骤
 * 1:定义需要的数据,收款人和付款人的姓名,转账金额
 * 2: 定义2个更新语句 update语句
 * 3:使用同一个数据库连接对象执行SQL语句
 * */
public class NoTransfer {
​
    public static void main(String[] args) throws SQLException {
        // 付款人的姓名tom
        String fromName="tom";
        // 收款人的姓名jerry
        String toName="jerry";
        //转账金额
        double money=500;
        //定义数据库操作需要的对象
        Connection connection=null;
        PreparedStatement preparedStatement=null;
​
        try {
            //从连接池中获取数据库连接
            connection= DataSourceUtils.getConnection();
​
            // 关闭事务的自动提交
            connection.setAutoCommit(false);
​
            //获取SQL语句的执行对象
            // 减钱的SQL语句
            String sql1="update account set money=money-? where name=?";
            preparedStatement=connection.prepareStatement(sql1);
​
            preparedStatement.setObject(1,money);
            preparedStatement.setObject(2,fromName);
            //执行SQL语句
            int num1 = preparedStatement.executeUpdate();
​
​
            //中间加一句话
            double a=1/0;
​
//            // 增加钱的SQL语句
            String sql2="update account set money=money+? where name=?";
​
            preparedStatement=connection.prepareStatement(sql2);
​
            preparedStatement.setObject(1,money);
            preparedStatement.setObject(2,toName);
            //执行SQL语句
            int num2=preparedStatement.executeUpdate();
​
            //打印出结果
            System.out.println("num1="+num1+"num2="+num2);
​
            //SQL语句执行完毕,没有发生异常,说明执行成功了,提交事务处理
            connection.commit();
​
​
        }catch (Exception e){
            e.printStackTrace();
            //如果出现异常,要回滚事务,注意 要用Exception去抓取,SQLException抓不到
            connection.rollback();
        }finally {
            DataSourceUtils.close(null,preparedStatement,connection);
        }
​
    }
}
 

执行成功:

package com.rocky.transfer;
import com.rocky.utils.DataSourceUtils;
​
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
​
/**
 * 实现转账功能
 * 功能的实现步骤
 * 1:定义需要的数据,收款人和付款人的姓名,转账金额
 * 2: 定义2个更新语句 update语句
 * 3:使用同一个数据库连接对象执行SQL语句
 * */
public class NoTransfer {
​
    public static void main(String[] args) throws SQLException {
        // 付款人的姓名tom
        String fromName="tom";
        // 收款人的姓名jerry
        String toName="jerry";
        //转账金额
        double money=500;
        //定义数据库操作需要的对象
        Connection connection=null;
        PreparedStatement preparedStatement=null;
​
        try {
            //从连接池中获取数据库连接
            connection= DataSourceUtils.getConnection();
​
            // 关闭事务的自动提交
            connection.setAutoCommit(false);
​
            //获取SQL语句的执行对象
            // 减钱的SQL语句
            String sql1="update account set money=money-? where name=?";
            preparedStatement=connection.prepareStatement(sql1);
​
            preparedStatement.setObject(1,money);
            preparedStatement.setObject(2,fromName);
            //执行SQL语句
            int num1 = preparedStatement.executeUpdate();
​
​
            //中间加一句话
//            double a=1/0;
​
//            // 增加钱的SQL语句
            String sql2="update account set money=money+? where name=?";
​
            preparedStatement=connection.prepareStatement(sql2);
​
            preparedStatement.setObject(1,money);
            preparedStatement.setObject(2,toName);
            //执行SQL语句
            int num2=preparedStatement.executeUpdate();
​
            //打印出结果
            System.out.println("num1="+num1+"num2="+num2);
​
            //SQL语句执行完毕,没有发生异常,说明执行成功了,提交事务处理
            connection.commit();
​
​
        }catch (Exception e){
            e.printStackTrace();
            //如果出现异常,要回滚事务,注意 要用Exception去抓取,SQLException抓不到
            connection.rollback();
        }finally {
            DataSourceUtils.close(null,preparedStatement,connection);
        }
​
    }
}
 

一次要执行多条SQL语句(增删改)的时候,会出现数据错误问题,因此必须要添加事务!

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

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

相关文章

Docker笔记 Docker架构与常用命令

大家好 我是积极向上的湘锅锅💪💪💪 1. Docker 架构 首先Docker由三大部分组成 Clients:客户端 通过docker的客户端操作命令,然后命令发送到daemon(守护进程)里面去执行操作Hosts:主机 主机包…

Linux——内核概念

一、内核 什么是内核? 计算机是由各种外部硬件设备组成的,如内存、cpu、硬盘等。如果每个应用都要和这些硬件对接通信协议,就太麻烦了,所以这个工作就由内核来负责。内核作为软件连接硬件设备的桥梁,使应用开发者只需…

C++常用库函数 1.缓冲区操作函数

函数名&#xff1a;memchr 函数原型&#xff1a;void *memchr(const void *buf&#xff0c;int c&#xff0c;sizet count)&#xff1b; 参数&#xff1a;buf 缓冲区的指针&#xff1b;c 查找的字符&#xff1b;count 检查的字符个数。 所需头文件&#xff1a;<cstring&g…

Python算法中的时间复杂度与空间复杂度

大家好&#xff0c;当谈论算法性能时&#xff0c;经常提到两个关键的衡量标准&#xff1a;时间复杂度和空间复杂度。时间复杂度指的是随着输入规模的增长&#xff0c;执行一个算法需要的时间&#xff0c;而空间复杂度指的是一个算法在执行过程中所使用的内存量。本文将详细探讨…

机器学习-sigmoid函数和relu函数-个人解读

机器学习-sigmoid函数和relu函数-个人解读 今天博主来解读一下sigmoid函数和relu函数&#xff0c;我觉得很多同学可能都知道这两个函数是什么&#xff0c;他们干什么的&#xff0c;他们有什么用&#xff0c;但是呢&#xff1f;我想这两个常用的激活函数内在的本质&#xff0c;…

Ai数字人——为写实而生

在当今数字化时代&#xff0c;人工智能技术正成为推动各行各业发展的重要驱动力。企业需要创新的解决方案&#xff0c;以提高效率、降低成本、增强客户体验并应对市场竞争。Ai数字人作为数字化转型的关键组成部分&#xff0c;具备强大的潜力和多样化的应用场景。 根据市场研究…

dbeaver配置SQL server连接

一、需要java jdk环境&#xff0c;我用的比较新 新的&#xff0c;那坑可就来了&#xff0c;先记着 cmd中 java --version出现版本号代表安装成功 二、dbeaver新建连接SQL server 点击测试连接会自动下载驱动&#xff0c;这个时候重点报错就开始出现了 错误一&#xff1a;DBea…

【已解决】matrix contains invalid numeric entries,记录bug修改

文章目录 摘要原因解决办法图像分类网络AlexNetVGGNetGooLeNet系列ResNetDenseNetSwin TransformerMAECoAtNetConvNeXtV1、V2MobileNet系列MPViTVITSWAEfficientNet系列MOBILEVITEdgeViTsMixConvRepLKNetTransFGConvMAEMicroNetRepVGGMaxViTMAFormerGhostNet系列DEiT系列MetaFo…

CMOS图像传感器——三星ISOCELL HP2技术解析

在之前有写文章介绍三星在2022年技术日活动分享的包括图像传感器在内的各种系统相关技术的最新改进和未来愿景,可以回顾下看看。 三星ISOCELL——2022 改进与展望_沧海一升的博客-CSDN博客三星ISOCELL最新改进和未来发展趋势https://blog.csdn.net/qq_21842097/article/detai…

Stable Diffusion AI 绘画入门指南

Stable Diffusion AI 绘画入门指南 市面上用的最多的AI绘画工具是 Stable-Diffusion&#xff08;SD&#xff09; 和 Midjourney&#xff08;Mid&#xff09;&#xff0c;SD是在本地运行的服务&#xff0c;开源&#xff0c;门槛高&#xff0c;但自控性极高。而Mid作为在线的服务…

Redis——基础篇(包含redis在云服务上的docker化安装和连接以及常用命令)

初识Redis Redis为键值型数据库&#xff0c;数据以键值形式存储。没有表&#xff0c;没有约束。 认识NoSQL mysql就是典型的关系型数据库(SQL)。 目的都是数据的增删改查&#xff0c;但数据存储方式不一样。 关系型和非关系型在结构上有差异 关系型的结构一般定好后就很少修…

基于 FFmpeg 的跨平台视频播放器简明教程(六):使用 SDL 播放音频和视频

系列文章目录 基于 FFmpeg 的跨平台视频播放器简明教程&#xff08;一&#xff09;&#xff1a;FFMPEG Conan 环境集成基于 FFmpeg 的跨平台视频播放器简明教程&#xff08;二&#xff09;&#xff1a;基础知识和解封装&#xff08;demux&#xff09;基于 FFmpeg 的跨平台视频…

【unity小技巧】委托(Delegate)的基础使用和介绍

文章目录 一、前言1. 什么是委托&#xff1f;2. 使用委托的优点 二、举例说明1. 例12. 例2 三、案例四、泛型委托Action和Func1. Action委托2. Func委托 五、参考六、完结 一、前言 1. 什么是委托&#xff1f; 在Unity中&#xff0c;委托&#xff08;Delegate&#xff09;是一…

2FRE-10B-35-G24电比例调速阀控制器

2FRE-06B-20-G24、2FRE-10B-35-G24、2FRE-10B-50-G24、2FRE-16B-80-G24、2FRE-16B-115-G24、2FRE-16B-160-G24电比例调速阀&#xff0c;通过给定电信号&#xff0c;控制阀出口流量大小&#xff0c;并且采用压力补偿原理&#xff0c;保证输出流量大小不受负载压力影响维持恒定;该…

低代码平台实用吗?有哪些大型企业在用低代码?

一、目前正在使用低代码平台的企业有哪些&#xff1f; &#xff08;例举一些各行业规模较大的企业&#xff09; 制造行业&#xff1a;施耐德电气、吉利汽车、科大能通、伟星集团、远东控股、一汽大众、老板电器、双菱电梯、君乐宝。 建筑行业&#xff1a;中国交建、筑福集团…

【C++STL】“vector“用法 入门必备 超详细

vector用法 什么是vector&#xff1f;vector的使用vector的定义(构造函数)vector iterator 的使用迭代器演示范围for vector 空间增长扩容机制 vector 的增删查改assignvector 迭代器失效问题。 &#x1f340;小结&#x1f340; &#x1f389;博客主页&#xff1a;小智_x0___0x…

关于将Leetcode上代码直接复制到自己环境中的问题

实例代码&#xff08;不考虑代码的优劣性&#xff09;&#xff1a; 注&#xff1a;我用的是一个在线平台的编译器 Problem1 NameError&#xff1a;name ‘List’ is not defined 解决方法&#xff1a;添加下面代码&#xff1a; from typing import ListProblem2 TypeError…

Android Glide预加载preload ,kotlin

Android Glide预加载preload ,kotlin val imageView findViewById<ImageView>(R.id.image_view)Glide.with(this).asBitmap().load(image_file.path).signature(ObjectKey(image_file.path)).addListener(object : RequestListener<Bitmap> {override fun onLoadF…

抓取唯美图库(BeautifulSoup)

使用BeautifulSoup 1、拿到主页面的源代码&#xff0c;然后提取到子页面的简介地址&#xff0c;href 2、通过href拿到子页面的内容。从子页面中找到导图片的下载地址 img -> src 3、下载图片 import requests from bs4 import BeautifulSoupurlhttps://www.umei.cc/bizhi…

qt 闹钟实现

实现一个闹钟 自定义时间 按下开始后 开始计时&#xff0c;结束计时会播报语音 widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTimer> #include <QTimerEvent> #include <QDateTime> #include <QTime> #include …