JDBC
JDBC(Java Database Connectivity)是 Java 语言用于与数据库进行交互的标准 API。
步骤
- 加载 JDBC 驱动程序:要连接特定的数据库,就需要加载对应的 JDBC 驱动程序。不同的数据库,其驱动程序也不同。
- 建立数据库连接:借助
DriverManager.getConnection()
方法,传入数据库的 URL、用户名和密码,从而建立与数据库的连接。 - 创建 Statement 对象:通过连接对象创建
Statement
对象,该对象用于执行 SQL 语句。 - 执行 SQL 语句:利用
Statement
对象执行 SQL 查询或更新操作。 - 处理结果集:若执行的是查询语句,就需要处理返回的结果集。
- 关闭资源:操作完成后,要关闭结果集、
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的数据在表里会报错。发生了幻读。