从入门到进阶:彻底掌握JDBC编程

从入门到进阶:彻底掌握JDBC编程

一、JDBC概述

1. JDBC是什么?

JDBC(Java DataBase Connectivity)是 Java 提供的一套用于操作关系型数据库的 API。它为 Java 应用程序与各种主流数据库(如 MySQL、Oracle、PostgreSQL、SQL Server 等)之间的通信提供了标准接口。

简单来说,JDBC 就是一座桥梁,它让 Java 程序可以像操作普通对象一样去执行数据库中的增删改查操作。开发者只需要通过 JDBC 提供的接口发送 SQL 语句,就可以完成对数据库的访问,而无需关心底层数据库的具体实现细节。

这套 API 主要包含了以下几类功能:

  • 连接数据库:通过驱动程序建立 Java 应用与数据库之间的连接;
  • 执行 SQL 语句:包括增删改查等各类 SQL 操作;
  • 处理结果集:通过 ResultSet 等接口处理查询返回的数据;
  • 事务管理:支持手动控制事务提交与回滚;
  • 异常处理:通过 SQLException 对各种数据库操作异常进行处理。

JDBC 是 Java EE 中最基础的持久化技术,许多高级的 ORM 框架(如 Hibernate、MyBatis)也都是建立在 JDBC 的基础之上。

2. JDBC的本质和工作机制

JDBC 的本质,其实是一套由 Sun 公司(现为 Oracle)定义的接口规范。这套规范标准化了 Java 程序如何与各种关系型数据库进行交互,其核心思想就是“面向接口编程”

具体来说,JDBC 定义了一系列通用的接口,例如:

  • Connection:数据库连接对象
  • Statement / PreparedStatement:执行 SQL 的对象
  • ResultSet:处理查询结果的对象

这些接口本身并不包含具体实现,它们只是一个规范的“承诺”。真正的实现是由各个数据库厂商来完成的。每个厂商会根据 JDBC 的规范,编写自己的一套驱动程序,并将其打包成一个 JAR 包(Java Archive),我们通常称之为 “数据库驱动”。

例如:

  • MySQL 提供了 mysql-connector-java 驱动;
  • Oracle 提供了 ojdbc 驱动;
  • PostgreSQL 提供了 postgresql 驱动。

当我们在 Java 程序中调用 JDBC 的接口时,真正被执行的,其实是这些驱动 JAR 包中对应的实现类。我们写的是面向 JDBC 接口的代码,运行时由驱动程序完成实际的数据库操作

这也正是 JDBC 强大之处的体现:应用程序只需依赖 JDBC 接口,不需要依赖具体的数据库实现。要切换数据库,只需更换驱动包和少量配置,无需改动核心代码,实现了良好的解耦和跨数据库的可移植性。

二、JDBC基础操作实践

1. 使用 JDBC 执行简单的 UPDATE 语句

以下是一个使用 JDBC 向数据库中执行更新操作的完整示例。我们以修改 user 表中某条记录的用户名为例:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;

public class JdbcUpdateExample {
    public static void main(String[] args) throws Exception {
        // 注册数据库驱动
        Class.forName("com.mysql.cj.jdbc.Driver"); // 加载 MySQL 驱动类

        // 获取数据库连接
        Connection conn = DriverManager.getConnection(
                "jdbc:mysql://localhost:3306/testdb", // 数据库地址和名称
                "root",                               // 用户名
                "password"                            // 密码
        );

        // 创建执行 SQL 的 Statement 对象
        Statement stmt = conn.createStatement(); // 创建 Statement 对象用于执行 SQL

        // 编写 SQL 语句
        String sql = "UPDATE user SET username='newName' WHERE id=1"; // 要执行的更新语句

        // 执行 SQL 语句
        int rows = stmt.executeUpdate(sql); // 执行更新操作,返回受影响的行数

        // 输出结果
        System.out.println("更新成功,受影响的行数:" + rows); // 打印受影响的行数

        // 释放资源
        stmt.close();  // 关闭 Statement
        conn.close();  // 关闭 Connection
    }
}

补充说明:

  • 在实际开发中,数据库的地址、用户名和密码建议不要硬编码到代码中,通常会使用配置文件进行管理。
  • executeUpdate 方法适用于 INSERTUPDATEDELETE 操作,返回一个整数表示影响的行数。
  • 切记操作完成后要 关闭资源,避免连接泄漏,这在高并发场景下尤其重要。

2. 使用 JDBC 执行 SELECT 查询,并封装到 User 对象中

以下示例展示如何查询数据库中 user 表的某条记录,并将其封装为一个 Java 对象。首先,我们定义一个 User 实体类:

// User.java
public class User {
    private int id;
    private String username;
    private String password;

    // 构造方法、getter 和 setter 略(可使用 IDE 自动生成)
    
    public String toString() {
        return "User{id=" + id + ", username='" + username + "', password='" + password + "'}";
    }
}

接着,我们编写一个简单的 JDBC 查询程序:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class JdbcSelectExample {
    public static void main(String[] args) throws Exception {
        // 注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");

        // 建立连接
        Connection conn = DriverManager.getConnection(
                "jdbc:mysql://localhost:3306/testdb",
                "root",
                "password"
        );

        // 创建 Statement
        Statement stmt = conn.createStatement();

        // 编写 SQL 查询语句
        String sql = "SELECT id, username, password FROM user WHERE id = 1";

        // 执行查询
        ResultSet rs = stmt.executeQuery(sql); // 返回结果集对象

        // 创建用户对象
        User user = null;

        // 处理结果集
        if (rs.next()) { // 如果查询结果有数据
            user = new User(); // 创建 User 实例
            user.setId(rs.getInt("id"));                 // 设置 id
            user.setUsername(rs.getString("username")); // 设置用户名
            user.setPassword(rs.getString("password")); // 设置密码
        }

        // 输出查询结果
        System.out.println("查询结果:" + user);

        // 释放资源
        rs.close();
        stmt.close();
        conn.close();
    }
}

补充说明:

  • ResultSetnext() 方法用于向下移动一行,初始指向的是第一行之前;
  • 使用 getXXX 方法获取指定列的数据,参数可以是列名或列索引;
  • 封装为 Java 对象是面向对象开发中常见的模式,便于后续处理、逻辑判断、传输等操作。

三、JDBC进阶应用

1. 深入理解结果集对象 ResultSet

在使用 JDBC 执行查询操作时,我们会经常接触到 ResultSet 接口。它代表 SQL 查询语句的结果集,也就是我们从数据库中读取的数据。

查询语句通常通过如下方式获取 ResultSet

ResultSet rs = statement.executeQuery("SELECT * FROM user");

这行代码的含义是:使用 Statement 对象执行一条 SELECT 语句,并将查询结果返回到 ResultSet 对象中。


(1)next() 方法详解

ResultSet 中的记录默认从第一行之前开始,需要通过调用 next() 方法将游标向前推进到第一条记录:

while (rs.next()) {
    // 读取当前行数据
}
  • rs.next() 返回 true 表示当前行有效,继续循环;
  • 返回 false 表示已经到达结果集末尾,结束循环;
  • 每次调用 next(),游标都会向下移动一行。

这一机制非常适合通过 while 循环逐行处理查询结果。


(2)getXXX() 方法使用

在移动到某一行后,我们可以通过 getXXX() 方法读取该行的每一列数据:

int id = rs.getInt("id");
String username = rs.getString("username");
String password = rs.getString("password");

常见的 getXXX() 方法有:

方法名返回类型示例字段类型
getInt(String)int整型字段(如 id)
getString(String)String字符串字段(如 name)
getDouble(String)double浮点型字段(如价格)
getDate(String)Date日期字段

参数既可以是 列名(推荐),也可以是 列索引(从 1 开始),如:

rs.getString(2); // 获取第二列的字符串内容

示例代码片段
while (rs.next()) {
    System.out.println("用户ID:" + rs.getInt("id"));
    System.out.println("用户名:" + rs.getString("username"));
    System.out.println("密码:" + rs.getString("password"));
}

2. 静态 SQL 与预编译 SQL(PreparedStatement)的对比

在 JDBC 中,我们可以通过 StatementPreparedStatement 来执行 SQL 语句。它们在使用方式和底层机制上有本质差异,以下我们通过示例代码对比、差异分析和优缺点总结,来全面理解这两种方式。


(1)示例代码对比

使用 Statement 的静态 SQL:

String username = "admin";
String password = "123456";

String sql = "SELECT * FROM user WHERE username = '" + username + "' AND password = '" + password + "'";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);

使用 PreparedStatement 的预编译 SQL:

String username = "admin";
String password = "123456";

String sql = "SELECT * FROM user WHERE username = ? AND password = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();

(2)差异解析
对比维度Statement(静态 SQL)PreparedStatement(预编译 SQL)
编写方式直接拼接 SQL 语句使用占位符 ? 进行参数绑定
安全性易受 SQL 注入攻击影响安全性高,自动转义输入防注入攻击
性能优化每次执行都重新编译SQL 语句预编译后可复用,提升性能
可读性与维护性拼接复杂,难以阅读和维护逻辑清晰,结构更清晰
数据类型支持参数需手动拼接,类型容易出错提供 setXXX() 方法,类型明确

(3)优缺点分析

Statement(静态 SQL)

  • 优点:
  • 简单直观,适合快速测试和临时性查询。
  • 缺点:
  • 安全性差,容易被 SQL 注入攻击;
  • 每次执行都需要解析和编译 SQL,效率低;
  • 拼接字符串容易出错,尤其是处理引号和空值。

PreparedStatement(预编译 SQL)

  • 优点:
  • 自动防止 SQL 注入;
  • 可读性好,代码更清晰;
  • SQL 语句编译一次可多次执行,性能更好;
  • 支持更复杂的数据类型(如二进制、大文本等)。
  • 缺点:
    • 初学者需要学习新的 API;
    • 某些极简 SQL 场景略显“冗长”。

总结来说,在实际开发中推荐优先使用 PreparedStatement,它不仅更安全,也更适合维护和扩展。Statement 更多用于临时查询或一些特殊场景下的动态 SQL 构造(但也建议结合工具类封装处理)。

3. SQL 注入及其防范

SQL 注入是一种常见的安全漏洞,攻击者通过在输入参数中注入恶意 SQL 片段,从而执行非法操作,如绕过登录验证、篡改数据、甚至删除数据库表。

例如,以下静态 SQL 写法容易被注入:

String sql = "SELECT * FROM user WHERE username = '" + input + "'";

如果用户输入 admin' OR '1'='1,则整个语句变为:

SELECT * FROM user WHERE username = 'admin' OR '1'='1'

这将绕过验证,返回所有用户信息。

如何防止?

  • 使用 PreparedStatement,参数通过占位符传递,自动处理转义;
  • 永远不要信任用户输入,必要时结合白名单校验;
  • 配置数据库账号权限,限制高危操作。

一句话总结永远用 PreparedStatement 替代拼接 SQL。

四、知识总结与思维导图

1.内容思维导图

在这里插入图片描述

2. 总结

本文从 JDBC 的基本概念讲起,带领你掌握了连接数据库、执行增删改查操作、处理结果集等基础能力。在进阶部分,我们深入探讨了 ResultSet 的使用细节、静态 SQL 与预编译 SQL 的对比,以及 SQL 注入的危害与防御手段。

核心记忆要点:

  • JDBC 是 Java 官方提供的数据库访问规范,驱动实现由厂商提供;
  • 数据库操作流程包括:加载驱动 → 获取连接 → 执行 SQL → 处理结果 → 释放资源;
  • 推荐使用 PreparedStatement 替代 Statement,更安全、更高效;
  • 时刻注意输入校验,避免 SQL 注入风险。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值