JDBC核心技术与预编译SQL实战
JDBC
一、JDBC 介绍
(一)概念
JDBC(Java DataBase Connectivity),是使用 Java 语言操作关系型数据库的一套 API 。本质上,它是 Sun 公司官方定义的一套操作所有关系型数据库的规范(接口) 。各个数据库厂商会去实现这套接口,提供数据库驱动 jar 包,我们使用 JDBC 编程时,真正执行的代码是驱动 jar 包中的实现类,借此可通过 Java 程序操作 MySQL、Oracle、SQL Server 等不同关系型数据库 。
(二)依赖引入(以 Maven 项目为例,操作 MySQL 时)
在项目的 pom.xml
文件中添加如下依赖,引入 MySQL 的 JDBC 驱动:
<dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>8.0.33</version>
</dependency>
二、JDBC 执行DML语句(以insert为例)
(一)需求
基于 JDBC 程序,执行 update 语句(如 update user set name = 'xiaohua' where id = 1
) 。
(二)代码实现(结合实际 Java 代码说明)
以下是使用 JDBC 操作数据库执行更新操作的完整 Java 代码及对应步骤解析,代码所在类为 jdbcTest
,位于 com.itxiaoli
包下:
package com.itxiaoli;import org.junit.Test;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;public class jdbcTest {/*** jdbc连接数据库入门程序*/@Testpublic void testUpdate() throws ClassNotFoundException, SQLException {// 1. 加载驱动(注册驱动)// 加载 MySQL 数据库的 JDBC 驱动类,让 Java 程序知道如何与 MySQL 数据库交互Class.forName("com.mysql.cj.jdbc.Driver");// 2. 获取数据库连接// 定义数据库连接的 URL,指定要连接的数据库为 *** ,运行在本地(localhost),端口 3306 String url = "***"; String username = "***"; // 数据库用户名,这里假设是 root String password = "***"; // 数据库密码 // 通过 DriverManager 的 getConnection 方法获取数据库连接对象Connection connection = DriverManager.getConnection(url, username, password);// 3. 获取 SQL 语句执行对象// 通过数据库连接对象创建 Statement 对象,用于执行 SQL 语句Statement statement = connection.createStatement();// 4. 执行 SQL 语句,并获取结果集// executeUpdate 方法返回值是 int 类型,表示受影响的记录行数int i = statement.executeUpdate("insert into *** values("**",**)"); System.out.println("sql语句执行完毕");// 5. 释放资源// 先关闭 Statement 对象,再关闭 Connection 对象,释放数据库连接相关资源statement.close(); connection.close(); }
}
步骤详细说明:
- 加载驱动(注册驱动):通过
Class.forName("com.mysql.cj.jdbc.Driver")
加载 MySQL 数据库的 JDBC 驱动类,使 Java 程序能够识别并使用对应的数据库驱动,建立与数据库的交互基础 。 - 获取数据库连接:使用
DriverManager.getConnection(url, username, password)
方法,传入数据库连接 URL(指定数据库位置、名称等)、用户名、密码,获取Connection
对象,代表 Java 程序与数据库的实际连接通道 。 - 获取 SQL 语句执行对象:利用
Connection
对象的createStatement()
方法创建Statement
对象,该对象可用于发送 SQL 语句到数据库执行 。 - 执行 SQL 语句:调用
Statement
对象的executeUpdate()
方法(针对增、删、改操作),传入要执行的 SQL 语句,执行后返回受影响的记录行数 。若执行查询操作则一般使用executeQuery()
方法 。 - 释放资源:操作完成后,依次关闭
Statement
和Connection
对象,释放占用的数据库资源,避免资源泄漏,保证程序的稳定性和高效性 。
通过上述步骤,就能借助 JDBC 完成 Java 程序对关系型数据库(这里以 MySQL 为例)的操作,实现数据的增、删、改、查等功能 。
三、JDBC 执行DQL语句(以select为例)
(一)需求说明
基于 JDBC 执行 select
语句,把查询结果封装到 User
对象中,最终整理到 List<User>
集合,实现数据从数据库到 Java 实体的映射 。示例 SQL 为 select * from user where username = 'daqiao' and password = '123456'
(实际应用中条件可动态处理 )。
(二)关键对象 - ResultSet(结果集对象)
通过 ResultSet rs = statement.executeQuery()
获取,用于承载查询返回的数据,核心方法:
next()
:将光标从当前位置前移一行,判断当前行是否有效行,返回boolean
。true
代表有效(有数据),false
代表无效(无数据),是遍历结果集的基础 。getXxx(...)
:获取数据,支持按列编号(如getInt(1)
)或列名(如getInt("id")
,推荐,可读性高 )获取,Xxx
随数据类型变化(getInt
、getString
等 )。
// 6. 处理结果集List<User> userList = new ArrayList<>();while (resultSet.next()) {// 创建User对象并设置属性User user = new User(resultSet.getInt("id"),resultSet.getString("username"),resultSet.getString("password"),resultSet.getString("name"),resultSet.getInt("age"));userList.add(user);}
JDBC程序执行DML语句? DQL语句?
•DML语句:int rowsAffected(受影响行数) = statement.executeUpdate();
•DQL语句:ResultSet(结果集) rs = statement.executeQuery();
DQL语句执行完毕结果集ResultSet解析?
•resultSet.next():光标往下移动一行
•resultSet.getXxx():获取字段数据
预编译SQL
一、预编译 SQL 概念与对比
(一)静态 SQL(参数硬编码)
在 JDBC 操作中,早期使用 Statement
执行 SQL 时,参数是直接硬编码在 SQL 语句里。例如:
Statement statement = connection.createStatement();
int i = statement.executeUpdate("update user set age = 25 where id = 1");
System.out.println("SQL执行完毕,影响的记录数为: " + i);
这种方式存在明显弊端,参数直接拼接,若输入内容不可控,易引发安全问题,且每次执行不同参数的同结构 SQL 都要重新解析、编译,性能欠佳 。
(二)预编译 SQL(参数动态传递)
通过 PreparedStatement
实现,先定义带有占位符(?
)的 SQL 模板,再动态设置参数。比如查询操作:
// 3. 准备SQL语句,使用占位符?定义参数位置,形成预编译SQL模板
String sql = "SELECT id, username, password, name, age FROM user WHERE username = ? AND password = ?";
// 4. 创建PreparedStatement对象并设置参数,同时完成预编译过程
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, "daqiao"); // 为第一个占位符设置值
preparedStatement.setString(2, "123456"); // 为第二个占位符设置值
// 5. 执行查询,由于预编译已做部分优化,执行效率更高
ResultSet resultSet = preparedStatement.executeQuery();
二、预编译 SQL 优势
(一)防止 SQL 注入,更安全
SQL 注入是攻击者通过控制输入,修改预先定义好的 SQL 语句,达到非法执行代码、攻击服务器的目的。比如后台登录场景,若用静态 SQL 拼接用户输入的账号密码,攻击者输入特殊构造内容(如 ' OR '1'='1
),可能绕过验证。而预编译 SQL 中,参数会被当作普通数据处理,不是 SQL 语句的一部分,有效抵御注入风险 。
(二)性能更高
当执行多条结构相同、仅参数不同的 SQL 时,预编译优势显著。以删除操作为例:
- 静态 SQL 方式:每次执行不同
id
的删除,如delete from user where id = 1;
、delete from user where id = 2;
等,MySQL 需对每条语句单独进行语法解析检查、优化、编译,多次重复流程 。 - 预编译 SQL 方式:只需定义一次
delete from user where id = ?;
,后续仅需设置不同id
参数。MySQL 对预编译后的 SQL 可缓存语法解析、优化结果,后续执行直接复用,减少重复开销,提升性能,尤其批量操作时效果明显 。
三、执行预编译 SQL 步骤(结合代码说明)
在 Java 中使用 JDBC 执行预编译 SQL ,通常包含以下流程(以查询用户信息为例,基于已获取 Connection
对象 connection
):
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;public class PrecompileSqlDemo {public void queryUser() {Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; try {// 假设已通过合适方式获取数据库连接 connection ,如前面提到的加载驱动、获取连接步骤// 3. 准备SQL语句,定义预编译的SQL模板,用?作为参数占位符String sql = "SELECT id, username, password, name, age FROM user WHERE username = ? AND password = ?"; // 4. 创建PreparedStatement对象,同时完成SQL预编译,后续设置参数即可preparedStatement = connection.prepareStatement(sql); preparedStatement.setString(1, "daqiao"); // 为第一个?占位符设置字符串参数,位置从1开始计数preparedStatement.setString(2, "123456"); // 为第二个?占位符设置字符串参数// 5. 执行查询,获取结果集。预编译后的执行,利用缓存等优化,性能更好resultSet = preparedStatement.executeQuery(); // 可遍历结果集处理数据,此处省略遍历逻辑...} catch (SQLException e) {e.printStackTrace();} finally {// 6. 释放资源,按照ResultSet、PreparedStatement、Connection的顺序关闭try {if (resultSet != null) resultSet.close();if (preparedStatement != null) preparedStatement.close();if (connection != null) connection.close();} catch (SQLException e) {e.printStackTrace();}}}
}
步骤解析:
- 准备 SQL 语句:编写包含占位符
?
的 SQL 模板,明确参数位置,告知数据库后续要动态传入的数据位置 。 - 创建
PreparedStatement
对象:调用connection.prepareStatement(sql)
,此时数据库会对 SQL 进行预编译处理,生成可复用的执行计划 。 - 设置参数:通过
preparedStatement
提供的setXxx
方法(如setString
、setInt
等,根据参数类型选择),为占位符按顺序设置实际值,保证参数类型匹配,防止错误 。 - 执行 SQL:执行
executeQuery
(查询操作)或executeUpdate
(增删改操作),由于预编译已做优化,执行效率相对静态 SQL 更高,且更安全 。 - 释放资源:操作完毕后,按
ResultSet
→PreparedStatement
→Connection
的顺序关闭资源,避免数据库连接泄漏,影响程序稳定性和性能 。
综上,预编译 SQL 凭借防止注入的安全性和重复执行时的高性能优势,成为 JDBC 操作中处理动态参数 SQL 语句的优选方式 。`