cats_eat_fish_头像
JDBC 入门:从「是什么」到「跑通第一条 SQL」封面图

JDBC 入门:从「是什么」到「跑通第一条 SQL」

1. 先说说 JDBC 是什么

如果你写过 Java,大概率听过 JDBC 这个词。它的全称是 Java Database Connectivity,中文一般叫「Java 数据库连接」。

用一句大白话概括:

JDBC 就是 Java 官方提供的一套「和数据库打交道」的标准接口。

你可以把它理解成一个万能插座。Java 程序是插头,各种数据库(MySQL、Oracle、PostgreSQL、SQL Server……)是不同规格的电器,而 JDBC 就是那个让它们能互相插上的统一标准。

这里有个关键点要划重点:JDBC 本身不是数据库,也不是某个具体的驱动,它只是一套「规范」。真正干活的是各家数据库厂商按照这套规范写出来的 JDBC 驱动(Driver)。

2. 为什么需要 JDBC

在 JDBC 出现之前,Java 程序员想连数据库,得针对每种数据库写一套代码。换个数据库,代码几乎要重写一遍,非常痛苦。

JDBC 解决的核心问题就是 「解耦」:

  • 对 Java 程序员:只需要学一套 API,就能操作几乎所有主流数据库;
  • 对数据库厂商:只要按 JDBC 规范提供驱动,就能被所有 Java 程序使用。

所以 JDBC 的价值可以总结成一句话:一套接口,连接所有数据库。

3. JDBC 的核心角色

在动手写代码前,先认识几个「演员」,后面写代码时你会反复见到它们:

角色作用
Driver数据库驱动,负责真正和数据库通信
DriverManager驱动管理器,帮我们获取数据库连接
Connection一个数据库连接,相当于「打通的一根电话线」
Statement / PreparedStatement用来执行 SQL 语句
ResultSet查询结果集,用来一行行读取数据

记住这张表,JDBC 的代码基本就是围绕它们转的。

4. JDBC 操作的五个核心步骤

这是本文最重要的部分,几乎所有 JDBC 代码都逃不出这五步:

  1. 加载并注册驱动
  2. 获取数据库连接(Connection)
  3. 创建执行 SQL 的对象(Statement / PreparedStatement)
  4. 执行 SQL 并处理结果(ResultSet)
  5. 释放资源(关闭连接)

下面用一张图串起来:

加载驱动

获取 Connection

创建 PreparedStatement

执行 SQL

处理 ResultSet

关闭资源

5. 完整代码示例

假设我们有一张 student 表,字段是 id、name、age。下面用 MySQL 8 演示一次完整查询。

5.1 准备依赖

如果用 Maven,先引入 MySQL 驱动:

<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>8.3.0</version>
</dependency>

5.2 编写代码

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

public class JdbcDemo {

    // 数据库连接信息
    private static final String URL =
            "jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=Asia/Shanghai";
    private static final String USER = "root";
    private static final String PASSWORD = "123456";

    public static void main(String[] args) {
        // 1. 加载驱动(JDBC 4.0 之后可省略,驱动会自动注册)
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        // 2~5 步:连接、执行、处理、关闭
        try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
             PreparedStatement ps = conn.prepareStatement(
                     "SELECT id, name, age FROM student WHERE age > ?")) {

            // 3. 设置参数
            ps.setInt(1, 18);

            // 4. 执行查询
            try (ResultSet rs = ps.executeQuery()) {
                // 5. 处理结果集
                while (rs.next()) {
                    int id = rs.getInt("id");
                    String name = rs.getString("name");
                    int age = rs.getInt("age");
                    System.out.println(id + " - " + name + " - " + age);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

几个值得注意的点:

  • try-with-resources 会自动关闭 Connection、PreparedStatement、ResultSet,比手动 finally 里 close 更省心;
  • PreparedStatement 用 ? 占位,能有效防止 SQL 注入,实际开发中优先用它,而不是 Statement;
  • URL 里的参数(如 serverTimezone)不同数据库、不同版本可能略有差异,报错时先检查这里。

6. 用 HikariCP 替换 DriverManager 获取连接

上面的示例里,每次调用 DriverManager.getConnection() 都会新建一条物理连接,用完再关掉。真实项目里这样写会非常慢,所以实际开发几乎都会用连接池。这里用目前最主流的 HikariCP 演示怎么改造。

6.1 引入依赖

<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>5.1.0</version>
</dependency>

6.2 配置连接池

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

import javax.sql.DataSource;

public class DataSourceFactory {

    private static final HikariDataSource DATA_SOURCE;

    static {
        HikariConfig config = new HikariConfig();
        // 注意:连接池用的是 jdbcUrl,不是 url
        config.setJdbcUrl("jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=Asia/Shanghai");
        config.setUsername("root");
        config.setPassword("123456");
        config.setDriverClassName("com.mysql.cj.jdbc.Driver");

        // 池中保持的最小空闲连接数
        config.setMinimumIdle(5);
        // 池中最大连接数
        config.setMaximumPoolSize(20);
        // 获取连接的超时时间(毫秒)
        config.setConnectionTimeout(3000);
        // 空闲连接存活时间(毫秒)
        config.setIdleTimeout(600000);
        // 连接最大存活时间(毫秒),建议比数据库的 wait_timeout 小一点
        config.setMaxLifetime(1800000);

        DATA_SOURCE = new HikariDataSource(config);
    }

    public static DataSource getDataSource() {
        return DATA_SOURCE;
    }
}

6.3 改造后的查询示例

改造的关键只有一处:把 DriverManager.getConnection(...) 换成 dataSource.getConnection(),其余代码几乎不用动。

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class HikariJdbcDemo {

    public static void main(String[] args) {
        DataSource dataSource = DataSourceFactory.getDataSource();

        String sql = "SELECT id, name, age FROM student WHERE age > ?";

        // 这里的 conn 是从池里借出来的,close() 时是「归还」而不是真正断开
        try (Connection conn = dataSource.getConnection();
             PreparedStatement ps = conn.prepareStatement(sql)) {

            ps.setInt(1, 18);

            try (ResultSet rs = ps.executeQuery()) {
                while (rs.next()) {
                    int id = rs.getInt("id");
                    String name = rs.getString("name");
                    int age = rs.getInt("age");
                    System.out.println(id + " - " + name + " - " + age);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

注意: conn.close() 在连接池里不是真的关闭连接,而是把连接还给池子,所以 try-with-resources 的写法依然要保留,否则连接会被一直占用、池子很快耗尽。

6.4 连接池相比每次新建连接的优势

  • 省掉反复建连的开销:建立一条数据库连接要经历 TCP 握手、身份认证等步骤,耗时往往是毫秒级甚至更高,连接池把连接复用起来,请求响应更快;
  • 控制并发连接数:maximumPoolSize 能防止程序在高并发下把数据库连接数打满,保护数据库不被拖垮;
  • 连接可复用、可管理:池子会自动检测失效连接、定期回收空闲连接,比手写 DriverManager 更稳;
  • 统一配置:账号、超时、池大小集中在一处,改配置不用翻遍代码。

一句话总结:DriverManager 适合学习和写小 Demo,真实项目请用连接池,HikariCP 是当下的首选。

7. 新手最容易踩的坑

  • 驱动没引入:报 ClassNotFoundException 或 No suitable driver,先检查依赖是否加对;
  • URL 写错:jdbc:mysql://主机:端口/数据库名,少一个斜杠都连不上;
  • 忘记关闭资源:连接池被占满,程序越跑越慢,务必用 try-with-resources;
  • 用 Statement 拼接 SQL:既容易出错又有注入风险,养成用 PreparedStatement 的习惯。

8. 原生 JDBC、MyBatis、JPA/Hibernate 怎么选

学完 JDBC,你很快会听到 MyBatis 和 JPA/Hibernate 这两个名字。它们本质上都是在 JDBC 之上做的封装,区别在于「封装到什么程度」。下面从五个维度做个对比:

维度原生 JDBCMyBatisJPA / Hibernate
代码量最多,连接、参数、结果集都要手写中等,SQL 写在 XML 或注解里,映射自动完成最少,多数 CRUD 一行方法即可
SQL 可控性完全可控,SQL 由你亲手写完全可控,SQL 仍由你写,只是交给框架执行较弱,默认自动生成 SQL,复杂查询需 JPQL 或原生 SQL 兜底
映射方式手动 rs.getXxx() 逐列取值半自动,结果列与对象属性做映射配置全自动,实体类与表通过注解/约定映射
学习成本低,API 少但要自己处理细节中,需掌握 SQL、映射配置与动态 SQL高,涉及实体生命周期、缓存、事务、懒加载等概念
适用场景教学、小工具、对 SQL 有极致要求的场景业务 SQL 复杂、需要精细调优的项目领域模型清晰、以增删改查为主的中大型项目

选型建议:

  • 先把原生 JDBC 跑通,它是理解后面所有框架的地基,遇到框架报错时你才知道底层发生了什么;
  • 业务里 SQL 复杂、需要精细优化,优先考虑 MyBatis,SQL 掌握在自己手里最踏实;
  • 项目以标准 CRUD 为主、追求开发效率,可以选 JPA/Hibernate,但要做好学习曲线较陡的心理准备。

9. 小结

最后用三句话收个尾:

  • JDBC 是 Java 访问数据库的标准接口,一套 API 通吃各种数据库;
  • 核心就是五步:加载驱动 → 获取连接 → 创建执行对象 → 执行并处理结果 → 关闭资源;
  • 实际开发优先用 PreparedStatement,安全又高效。

把上面的示例跑通一遍,你就已经跨过了 JDBC 的第一道门槛。接下来可以继续了解 连接池(如 Druid、HikariCP) 和 ORM 框架(如 MyBatis),它们都是在 JDBC 之上做的封装。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/cats_eat_fish_/article/details/170780616

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--