JDBC和MySql中的事务

JDBC

JDBC(Java Database Connectivity)是 Java 语言用于与数据库进行交互的标准 API。

步骤

  1. 加载 JDBC 驱动程序:要连接特定的数据库,就需要加载对应的 JDBC 驱动程序。不同的数据库,其驱动程序也不同。
  2. 建立数据库连接:借助DriverManager.getConnection()方法,传入数据库的 URL、用户名和密码,从而建立与数据库的连接。
  3. 创建 Statement 对象:通过连接对象创建Statement对象,该对象用于执行 SQL 语句。
  4. 执行 SQL 语句:利用Statement对象执行 SQL 查询或更新操作。
  5. 处理结果集:若执行的是查询语句,就需要处理返回的结果集。
  6. 关闭资源:操作完成后,要关闭结果集、Statement对象和数据库连接,以释放资源。

Maven 项目示例代码

pom.xml添加依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.qcby</groupId>
    <artifactId>jdbcdemo20250401</artifactId>
    <version>1.0-SNAPSHOT</version>

        <dependencies>
            <dependency>
                <groupId>com.mysql</groupId>
                <artifactId>mysql-connector-j</artifactId>
                <version>8.0.33</version>
            </dependency>
        </dependencies>
</project>

 JdbcDemo

package com.qcby.jdbcdemo;

import java.sql.*;

public class JdbcDemo {
    //    主方法,主函数,程序入口
    public static void main(String[] args) throws SQLException {
        Connection connection = null;
        ResultSet resultSet = null;
        Statement statement = null;

        try {
            //        加载驱动2中 反射加载 直接加载
            //        1
            Class.forName("com.mysql.cj.jdbc.Driver");
            //            2
            //            DriverManager.registerDriver(new Driver());
            //            获取链接 用户名 密码 url 驱动
            //            主协议jdbc 二层协议mysql 主机localhost/127.0.0.1 默认端口号 3306 数据库 test
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8", "root", "root");
            connection.setAutoCommit(false);
            //          编写sql
            String sql = "UPDATE  t_account SET money=money-1000 where username='冠希'";
            String sql1 = "UPDATE  t_account SET money=money+1000 where username='美美'";

            //            获取执行sql的对象
            //            不安全 注入问题
            statement = connection.createStatement();
            int update=statement.executeUpdate(sql);
           // int a=10/0;
            int update1=statement.executeUpdate(sql1);
            connection.commit();
            System.out.println(update);
            System.out.println(update1);
        } catch (Exception e) {
//            回滚
            connection.rollback();
            // 打印异常信息
            e.printStackTrace();
            // 可以选择抛出更有意义的异常
            throw new RuntimeException("数据库操作出错", e);
        } finally {
            //            关闭链接
            try {
                if (resultSet != null) {
                    resultSet.close();
                }
                if (statement != null) {
                    statement.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

 包声明和导入语句

package com.qcby.jdbcdemo;

import java.sql.*;

package com.qcby.jdbcdemo;:声明该 Java 类所在的包为com.qcby.jdbcdemo。
import java.sql.*;:导入java.sql包下的所有类,这些类提供了 JDBC 相关的接口和类,用于与数据库进行交互。 

类定义和主方法

public class JdbcDemo {
    public static void main(String[] args) throws SQLException {
        Connection connection = null;
        ResultSet resultSet = null;
        Statement statement = null;

public class JdbcDemo:定义了一个名为JdbcDemo的公共类。
public static void main(String[] args) throws SQLException:程序的入口方法,当运行该 Java 程序时,会从这个方法开始执行。throws SQLException表示该方法可能会抛出SQLException异常。
Connection connection = null;、ResultSet resultSet = null;、Statement statement = null;:声明并初始化了三个变量,分别用于表示数据库连接、结果集和执行 SQL 语句的对象。初始值都设为null,后续会进行赋值。 

加载数据库驱动

try {
    Class.forName("com.mysql.cj.jdbc.Driver");
    // DriverManager.registerDriver(new Driver());

Class.forName("com.mysql.cj.jdbc.Driver");:使用反射机制加载 MySQL 的 JDBC 驱动类。在 Java 中,要与 MySQL 数据库进行交互,需要先加载对应的驱动类。
DriverManager.registerDriver(new Driver());:这是另一种加载驱动的方式

 获取数据库链接

connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8", "root", "root");
connection.setAutoCommit(false);

DriverManager.getConnection:通过DriverManager类的getConnection方法获取与数据库的连接。参数分别为数据库的 URL、用户名和密码。
jdbc:mysql://localhost:3306/test:表示连接本地(localhost)的 MySQL 数据库,端口号为3306,数据库名为test。
useUnicode=true&characterEncoding=UTF-8:设置使用 Unicode 字符集,并且字符编码为 UTF - 8,以避免中文乱码问题。
root:数据库的用户名。
root:数据库的密码。

connection.setAutoCommit(false);:将数据库连接的自动提交模式设置为false,意味着后续的 SQL 操作不会自动提交,需要手动调用commit()方法提交事务。

编写 SQL 语句

String sql = "UPDATE  t_account SET money=money-1000 where username='冠希'";
String sql1 = "UPDATE  t_account SET money=money+1000 where username='美美'";

这两条 SQL 语句用于实现转账功能,第一条语句将冠希的账户余额减少1000,第二条语句将美美的账户余额增加1000。

获取执行 SQL 的对象并执行 SQL 语句

statement = connection.createStatement();
int update = statement.executeUpdate(sql);
 // int a = 10 / 0;
int update1 = statement.executeUpdate(sql1);
connection.commit();
System.out.println(update);
System.out.println(update1);

statement = connection.createStatement();:通过Connection对象创建一个Statement对象,用于执行 SQL 语句。
statement.executeUpdate(sql);:执行UPDATE类型的 SQL 语句,并返回受影响的行数。
int a = 10 / 0;:这是一个故意制造的异常,用于模拟在转账过程中出现错误的情况。
statement.executeUpdate(sql1);:如果没有异常,继续执行第二条UPDATE语句。
connection.commit();:如果所有 SQL 语句都执行成功,调用commit()方法提交事务,将对数据库的修改永久保存。
System.out.println(update); 和 System.out.println(update1);:打印两条UPDATE语句受影响的行数。

异常处理

} catch (Exception e) {
    connection.rollback();
    e.printStackTrace();
    throw new RuntimeException("数据库操作出错", e);
}

catch (Exception e):捕获在try块中抛出的所有异常。
connection.rollback();:如果发生异常,调用rollback()方法回滚事务,撤销之前执行的所有 SQL 操作,保证数据的一致性。
e.printStackTrace();:打印异常的堆栈信息,方便调试。
throw new RuntimeException("数据库操作出错", e);:抛出一个新的RuntimeException异常,包含错误信息和原始异常。

关闭资源

finally {
    try {
        if (resultSet != null) {
            resultSet.close();
        }
        if (statement != null) {
            statement.close();
        }
        if (connection != null) {
            connection.close();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

finally块:无论try块中是否发生异常,finally块中的代码都会执行。

通过connection.setAutoCommit(false);后续的 SQL 操作不会自动提交。需要手动调用commit()方法提交事务。但在着之前出现了除零异常。会调用connection.rollback();回滚事务。数据库的数据不会发生变化。如果不这样操作的话,会出现数据错误,两个账户无法保证一致性。会出现,一个账户剪掉1000块钱,另一个却没有变化。

事务

 MySQL 事务是一组不可分割的 SQL 操作序列,这些操作要么全部成功执行,要么全部不执行。

特性:

原子性(Atomicity):事务中的所有操作要么全部完成,要么全部不完成,不会停留在中间某个环节。如果事务执行过程中发生错误,会被回滚到事务开始前的状态。
一致性(Consistency):事务执行前后,数据库从一个一致性状态转换到另一个一致性状态。例如,在转账操作中,无论事务是否成功,两个账户的总金额应该保持不变。事务前后数据的完整性必须保持一致
隔离性(Isolation):多个事务并发执行时,一个事务的执行不应该影响其他事务的执行MySQL 提供了不同的隔离级别来控制事务之间的相互影响。隔离性由隔离级别保障。
持久性(Durability):事务一旦提交,它对数据库的改变就是永久性的,即使数据库发生故障也不会丢失。

事务的基本操作

在 MySQL 中,可以使用以下语句来管理事务:

  • START TRANSACTION:开始一个新的事务。
  • COMMIT:提交事务,将事务中所有的操作永久保存到数据库中。
  • ROLLBACK:回滚事务,撤销事务中所有未提交的操作。

事务隔离级别

(1)未提交读(Read Uncommitted):允许脏读,即允许一个事务看到其他事务未提交的修改。这种隔离级别最低,性能最高,但一致性最差。
(2)提交读(Read Committed):只允许一个事务看到其他事务已经提交的修改。这种隔离级别可以防止脏读,但不能防止不可重复读和幻读。
(3)可重复读(Repeatable Read):确保如果在一个事务中执行两次相同的SELECT语句,都能得到相同的结果。这种隔离级别可以防止脏读和不可重复读,但不能完全防止幻读(在某些数据库系统中,如MySQL的InnoDB引擎,通过间隙锁等技术可以进一步防止幻读),MySQL默认事务级别。
(4)串行化(Serializable):将事务完全隔离,使得它们按顺序执行。这种隔离级别最高,一致性最好,但性能最低。

 实例:

开启两个cmd窗口。分别打开数据可选择同一个数据表。假设为a和b

 a和b选择数据库和展示数据表里的内容。

a查看隔离级别。是未提交读(Read Uncommitted)

 b的隔离级别

a和b开启事务

b执行sql语句

 

a查看数据库变化。发现出现了脏读。

b提交事务再查看和b还未提交时。a的查询结果一致。

a提高一级 提交读(Read Committed)

重复之前的操作,b执行sql语句并且还未提交。

a此时也进入了事务。查询发现没有变化。未发生脏读。此时b再提交。

 a还不要提交,再查询数据库。发现数据变化。出现了不可重复读。

 a提升到可重复读(Repeatable Read)。

a和b都开启事务并先查询一次数据。

b插入一条数据 

b提交 ,a再查询。发现数据没变化 。

a要是还想插入一条相同id的数据在表里会报错。发生了幻读。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值