JDBC(Java Database Connectivity)

详情

一、JDBC 是什么?

JDBC(Java Database Connectivity) 是 Java 提供的一套访问数据库的标准 API 规范,它允许 Java 应用程序与各种关系型数据库进行交互。JDBC 是 Java 标准库(java.sql 和 javax.sql 包)的一部分,属于 Java SE 的核心组件。

核心作用:提供统一的接口,屏蔽不同数据库之间的差异,使开发者能以一致的方式操作 MySQL、PostgreSQL、Oracle、SQL Server 等数据库。

二、没有 JDBC 前

在 JDBC 没有出现之前,怎么访问各个数据库厂商提供的数据库呢?

1. 直接调用本地 C/C++ 库(通过 JNI)

很多早期数据库(如 Oracle、Sybase、Informix)只提供了 C 语言的客户端库(例如 Oracle 的 OCI - Oracle Call Interface)。Java 程序若想连接这些数据库,必须:

  • 编写 JNI(Java Native Interface)代码;
  • 调用本地动态链接库(.dll 或 .so);
  • 处理复杂的内存管理和平台兼容性问题。
// 假设有一个 Oracle 的 native wrapper
public class OracleNativeAccess {
    static {
        System.loadLibrary("oracle_oci_wrapper"); // 加载本地库
    }
    public native ResultSet query(String sql); // 声明 native 方法
}

这种方式不仅开发复杂,而且丧失了 Java “一次编写,到处运行”的优势。

2. 使用数据库厂商私有的 Java API

一些数据库厂商(如 Sybase、Informix)在 JDBC 出现前就提供了自己的 Java 类库,但这些 API 各不相同,互不兼容。

  • Sybase jConnect(早期版本) 可能这样用:
    SybDriver driver = new SybDriver();
    Connection conn = driver.connect("sybase:Tds:localhost:5000", props);
  • Oracle 的早期 Java 接口(非 JDBC):
    OracleConnection conn = new OracleConnection("user", "password");
    OracleStatement stmt = conn.createStatement();
    OracleResultSet rs = stmt.executeQuery("SELECT * FROM emp");

每换一个数据库,就要重写整个数据访问层。

3. 通过中间件或网关协议(如 ODBC 桥接)

有些系统通过 ODBC(Open Database Connectivity,微软的 Windows 数据库接口标准)间接访问数据库。Java 程序需借助 JDBC-ODBC Bridge(虽然它本身是 JDBC 的一部分,但在 JDBC 成熟前常被当作“变通方案”)。

但在纯 Java 环境中,这种桥接依赖操作系统安装的 ODBC 驱动,且性能差、跨平台性弱。

// 伪代码:调用系统命令启动 ODBC 查询工具,解析输出
Process p = Runtime.getRuntime().exec("odbc_query_tool -d mydb -q 'SELECT ...'");
BufferedReader output = new BufferedReader(new InputStreamReader(p.getInputStream()));
// 手动解析文本结果...

4. 基于 Socket 的自定义协议

某些轻量级或嵌入式数据库(如早期的 dBASE、Paradox)甚至没有标准网络接口,开发者需:

  • 直接通过 TCP/UDP 与数据库服务器通信;
  • 手动序列化 SQL 请求;
  • 解析二进制或文本响应格式。
Socket socket = new Socket("dbserver", 12345);
OutputStream out = socket.getOutputStream();
out.write("SELECT name FROM users\n".getBytes());
// 然后按特定格式读取返回的数据流...

这种方式极易出错,且难以维护。

5. 文件系统模拟数据库

在更早期,有些“数据库”其实就是结构化文件(如 CSV、固定宽度文本、dBASE .dbf 文件)。Java 程序直接读写文件,自行实现查询逻辑。

BufferedReader reader = new BufferedReader(new FileReader("users.dbf"));
String line;
while ((line = reader.readLine()) != null) {
    if (line.contains("John")) { // 手动“查询”
        System.out.println(line);
    }
}

通过以上的示例,可以看见如果没有JDBC:

  1. 每个数据库都要写一套专属代码,切换数据库几乎等于重写数据层,代码无法复用,维护成本爆炸。
  2. Java 失去“一次编写,到处运行”的优势,数据库访问逻辑被绑定到具体厂商实现上。
  3. 性能差,实现麻烦。

三、为什么使用 JDBC?

JDBC 的核心价值是:抽象 + 标准化

  • 定义了 ConnectionStatementResultSet 等接口。
  • 各数据库厂商提供自己的 JDBC 驱动(实现这些接口)。
  • 你的代码只依赖标准接口,不依赖具体数据库

JDBC 做到了:

  1. 标准化访问:无论底层是哪种数据库,只要提供对应的 JDBC 驱动,就能用相同的方式操作。
  2. 跨平台性:基于 Java 的“一次编写,到处运行”特性。
  3. 轻量级:无需额外依赖(除了数据库驱动),适合小型项目或学习用途。
  4. 可控性强:直接操作 SQL,对性能调优、事务控制等有完全掌控权。
  • JDBC = 标准规范(接口)
  • JDBC 驱动 = 数据库厂商对这些接口的具体实现 典型的 “面向接口编程” + “SPI(Service Provider Interface)” 设计思想。

四、JDBC 的核心组件

组件说明
DriverManager管理 JDBC 驱动,用于获取数据库连接
Connection表示与数据库的连接
Statement / PreparedStatement用于执行 SQL 语句
ResultSet查询结果集,可遍历获取数据
SQLException所有 JDBC 异常的父类

五、JDBC 使用步骤(标准流程)

  1. 加载数据库驱动(JDBC 4.0+ 可省略)
  2. 建立数据库连接(DriverManager.getConnection()
  3. 创建 Statement 或 PreparedStatement
  4. 执行 SQL 语句
  5. 处理结果集(如果是查询)
  6. 关闭资源(Connection, Statement, ResultSet)

最佳实践:使用 try-with-resources 自动关闭资源,防止内存泄漏。

六、JDBC 示例代码

import java.sql.*;
import java.util.ArrayList;
import java.util.List;
 
public class JdbcExample {
 
    public void queryArticles(int authorId, String date) {
        String url = "jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC";
        String user = "root";
        String password = "password";
 
        // 注意:JDBC 4.0+ 会自动加载驱动,无需 Class.forName()
        String sql = "SELECT id, title, content, create_time FROM article " +
                     "WHERE author_id = ? AND create_time > ?";
 
        try (Connection conn = DriverManager.getConnection(url, user, password);
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
 
            // 使用 PreparedStatement 防止 SQL 注入
            pstmt.setInt(1, authorId);
            pstmt.setDate(2, Date.valueOf(date)); // 假设 date 格式为 "yyyy-MM-dd"
 
            try (ResultSet rs = pstmt.executeQuery()) {
                List<Article> articles = new ArrayList<>();
                while (rs.next()) {
                    Article article = new Article();
                    article.setId(rs.getInt("id"));
                    article.setTitle(rs.getString("title"));
                    article.setContent(rs.getString("content"));
                    article.setCreateTime(rs.getDate("create_time"));
                    articles.add(article);
                }
 
                System.out.println("Query SQL ==> " + sql);
                System.out.println("Query Result:");
                articles.forEach(System.out::println);
            }
        } catch (SQLException e) {
            System.err.println("Database error: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

七、JDBC 的局限性

问题说明
无连接池每次都新建连接,性能差
代码冗余大量样板代码(打开/关闭连接、异常处理)
易出错手写 SQL 容易拼错、漏关资源
无对象映射需手动将 ResultSet 转为 Java 对象
不支持高级特性如缓存、懒加载、关联查询等

解释:

JDBC是Java 访问数据库的标准 API 规范(一套接口),不包含连接池。

  • 原生 JDBC 每次调用 DriverManager.getConnection() 都会:
    • 创建新 TCP 连接
    • 进行身份认证
    • 初始化会话
  • 这个过程非常耗时(可能几十毫秒),而执行 SQL 可能只要 1ms。
  • 在高并发下,频繁创建/销毁连接会导致:
    • 性能瓶颈
    • 数据库连接数耗尽(报错 “Too many connections”)

因此实际开发中,JDBC + HikariCP/Druid 是常见组合;更高层则用 MyBatis 或 Spring Data JPA

  • HikariCP/Druid/C3P0:这些工具解决 “连接效率” 问题 → 连接池
  • MyBatis(SQL 映射框架)/Hibernate(ORM 框架):解决 “开发效率” 问题

关联网络

演化日志

  • v0.1 (2026-01-31):初始版本
  • v0.2 (2026-02-01):补充没有 JDBC 的情况说明

待办事项

  • 使用HikariCP(高卡利)DruidC3P0
  • 关掉隐性事务,合并成一个事务解决
  • 学习 DataSource 接口(比 DriverManager 更现代的连接获取方式)
  • 了解事务管理(conn.setAutoCommit(false) + commit()/rollback()
  • 尝试整合 HikariCP
  • “面向接口编程” + “SPI(Service Provider Interface)” 设计思想。
  • ORM / SQL 映射框架?
  • TCP 连接,身份认证,初始化会话
  • 编写 JNI(Java Native Interface)代码;
  • 调用本地动态链接库(.dll 或 .so);
  • ODBC
  •  JDBC-ODBC Bridge
  • TCP/UDP