1. 什么是 JDBC
JDBC(Java Data Base Connectivity, Java 数据库连接)是 Java 程序和数据库之间的桥梁,包含了一套 Java 定义的用于执行 SQL 语句的接口,使开发者能够编写数据库的程序。JDBC 的主要作用是:与数据库建立连接、发送 SQL 语句和处理数据库执行结果。
JDBC 是 Java 连接数据库的标准 API。本文介绍了 JDBC 的工作原理、具体使用步骤(加载驱动、建立连接、创建 Statement、执行 SQL、处理结果、释放资源)以及核心类和接口(DriverManager、DataSource、Connection、Statement、PreparedStatement、ResultSet)。重点讲解了 SQL 注入问题及 PreparedStatement 的解决方案,并对比了 DriverManager 与 DataSource 的区别。

JDBC(Java Data Base Connectivity, Java 数据库连接)是 Java 程序和数据库之间的桥梁,包含了一套 Java 定义的用于执行 SQL 语句的接口,使开发者能够编写数据库的程序。JDBC 的主要作用是:与数据库建立连接、发送 SQL 语句和处理数据库执行结果。
首先回顾一下我们使用 Navicat 客户端操作 MySQL 数据库的过程: a. 连接到数据库 b. 发送 SQL 语句 c. 执行 SQL 语句 d. 显示得到的结果 e. 关闭连接
同样如果使用程序操作数据库也会经历以上几步,但是不同的数据库对于同一个操作不论是协议还是参数都各有不同,如果让程序员自己去实现,那就必须针对不同的数据库进行编码实现,这个工作量和维护成本显然太大。Java 采取的做法是把以上操作步骤定义了相应的接口,具体的实现交给数据库厂商去做,Java 程序员只需要按照需要调用接口中定义的方法即可,这样不论使用什么数据库,都对于 Java 程序没有任何影响,即便是换一个数据库,也只需要换一下相应厂商的实现依赖。
JDBC 工作原理简洁地概括为:
在 Maven 仓库搜索 MySQL,找到最新版的驱动包 mvnrepository.com
修改 pom.xml 文件:在 Maven 工程中的 pom.xml 文件的 dependencies 标签中添加 MySQL 依赖
<dependencies>
<!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
</dependency>
</dependencies>
首先给大家介绍一下 URL,在数据库连接中,URL(Uniform Resource Locator,统一资源定位符)参数起着关键作用。以下是对数据库 URL 参数的解释说明:
一。通用结构
jdbc:mysql://[host][:port]/[database][?propertyName1][=propertyValue1][&propertyName2][=propertyValue2]...
二。各部分含义
• 使用驱动管理类 DriverManager 的静态方法获取数据库连接
• 通过数据源 DataSource 对象获取 (更推荐使用这种方式建立数据库连接)
Statement 是用于执行静态 SQL 语句并返回执行结果的对象。
DriverManager 与 DataSource 的区别
Connection 接口代表 java 端与指定数据库之间的连接,Connection 接口的常用方法及说明如下表:
| 方法 | 功能描述 |
|---|---|
| createStatement() | 创建 Statement 对象 |
| createStatement(int resultSetType, int resultSetConcurrency) | 创建 Statement 对象,Statement 对象被用来生成一个具有给定类型、并发性和可保存性的 ResultSet 对象 |
| prepareStatement() | 创建预处理 prepareStatement 对象 |
| prepareCall(String sql) | 创建一个 CallableStatement 对象来调用数据库存储过程 |
| isReadOnly() | 查看当前 Connection 对象的读写模式是否是只读模式 |
| setReadOnly() | 查看当前 Connection 对象的读写模式,默认为非只读模式 |
| commit() | 使上一次提交/回滚后进行的更改成为持久更改,并释放此 Connection 对象当前持有的数据库锁 |
| rollback() | 取消在当前事务中进行的更改,并释放此 Connection 对象当前持有的数据库锁 |
| close() | 立即释放此 Connection 对象的数据库和 JDBC 资源,而不是等待它们被自动释放 |
用于执行静态 SQL 语句并返回执行结果
| 方法 | 功能描述 |
|---|---|
| execute(String sql) | 执行静态的 SELECT 语句,该语句可能返回多个结果集 |
| executeQuery(String sql) | 执行给定的 SQL 语句,该语句返回单个 ResultSet 对象 |
| clearBatch() | 清空此 Statement 对象的当前 SQL 命令列表 |
| executeBatch() | 将一批命令提交给数据库来执行,如果全部命令执行成功,则返回更新计数组成的数组,数组元素的排序与 SQL 语句的添加顺序对应 |
| addBatch() | 将给定的 SQL 命令添加到此 Statement 对象的当前命令列表中,如果驱动程序不支持批量处理,将抛出异常 |
| close() | 释放 Statement 对象占用的数据库和 JDBC 资源 |
| executeUpdate() | 执行前面包含的参数的动态 INSERT、UPDATE 或 DELETE 语句 |
由于只能执行静态语句,所以这里会有一个问題,假设一个语句中需要动态的参数,比如 where 子句中的条件,那么只能通过字符串拼接的方式组装完成的 SQL 语句,比如:
String sql = "select * from student where name = '" + name + "' and class_id = " + classId;
字符串拼接形式构造 SQL 语句时,如果不处理参数中的特殊字符就会造成 SQL 注入,这是一个非常严重的安全性问题。
SQL 注入即是指 web 应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在 web 应用程序中事先定义好的查询语句的结尾上添加额外的 SQL 语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。
示例: • 姓名条件宋江和班级编号条件 1 是动态的参数
# 正常的查询,没有符合的结果
mysql> select * from student where name ='宋江' and class_id =1;
Empty set (0.01 sec)
• 构造恶意参数来执行非法的 SQL,把正常的条件值宋江替换为'or 1 = 1;–
# 查询出来了所有的记录,此时学生表中的数据全部都将泄漏
mysql> select * from student where name ='' or 1=1;-- 宋江' and class_id =1;
+----+----------+--------+------+--------+-------------+----------+
| id | name | sno | age | gender | enroll_date | class_id |
+----+----------+--------+------+--------+-------------+----------+
| 1 | 唐三藏 | 100001 | 18 | 1 | 1986-09-01 | 1 |
| 2 | 孙悟空 | 100002 | 18 | 1 | 1986-09-01 | 1 |
| 3 | 猪悟能 | 100003 | 18 | 1 | 1986-09-01 | 1 |
| 4 | 沙悟净 | 100004 | 18 | 1 | 1986-09-01 | 1 |
| 5 | 宋江 | 200001 | 18 | 1 | 2000-09-01 | 2 |
| 6 | 武松 | 200002 | 18 | 1 | 2000-09-01 | 2 |
| 7 | 李逵 | 200003 | 18 | 1 | 2000-09-01 | 2 |
| 8 | 不想毕业 | 200004 | 18 | 1 | 2000-09-01 | 2 |
+----+----------+--------+------+--------+-------------+----------+
8 rows in set (0.01 sec)
预编译 SQL 语句对象,SQL 语句被预编译并存储在 PreparedStatement 对象中,可以使用该对象多次执行 SQL 语句,同时可以解决 SQL 注入问题。
示例:通过 Connection 对象获取到 PreparedStatement 对象,需要传入 SQL 模板,动态参数用占位符?表示。
// 获取了个处理 SQL 的 PrepareStatement 对象
PreparedStatement preparedStatement = connection.prepareStatement("select id, name, sno, age, gender, enroll_date, class_id from student where name =? and class_id =?");
// 用真实值去替换占位符
preparedStatement.setString(1, "宋江");
preparedStatement.setLong(2, 2);
ResultSet 接口类似于一个临时表,用来暂时存放对数据库中的数据执行查询操作后的结果,ResultSet 对象具有指向当前数据行的指针,指针开始的位置在第一条记录上,通过 next() 方法可向下移动指针,ResultSet 接口的常用方法以及说明如下表:

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online