当前位置: 代码网 > it编程>编程语言>Java > 从零开始手写JDBC连接数据库的详细指南

从零开始手写JDBC连接数据库的详细指南

2025年07月30日 Java 我要评论
前言在 java 开发中,数据库是存储和管理数据的核心组件。而 jdbc(java database connectivity)是 java 程序与数据库交互的标准 api,它允许 java 应用程序

前言

在 java 开发中,数据库是存储和管理数据的核心组件。而 jdbc(java database connectivity)是 java 程序与数据库交互的标准 api,它允许 java 应用程序连接各种关系型数据库(如 mysql、oracle、postgresql 等),并执行 sql 语句。虽然现代开发中常使用 orm 框架(如 mybatis、hibernate)简化数据库操作,但理解 jdbc 的底层原理对于深入掌握数据库编程至关重要。

本文将带你手写一个完整的 jdbc 连接数据库的示例,涵盖从环境准备到代码实现的每一个细节,力求做到“越详细越好”。

一、环境准备

在开始编码之前,确保你的开发环境已准备好以下工具和资源:

1.jdk(java development kit)

  • 确保已安装 jdk 8 或更高版本。jdbc 是 java se 的一部分,因此 jdk 自带 jdbc api。
  • 检查方法:在命令行输入 java -versionjavac -version

2.数据库

  • 本文以mysql为例(版本 8.0+)。你需要安装并启动 mysql 服务。
  • 确保你知道 mysql 的主机地址(通常是localhost127.0.0.1)、端口号(默认3306)、数据库名称用户名密码

3.数据库驱动(jdbc driver)

jdbc 需要特定数据库的驱动程序来实现与数据库的通信。对于 mysql,需要mysql-connector-java驱动。

下载驱动

  • 访问 mysql connector/j 官方下载页面。
  • 选择与你的 mysql 版本和 java 版本兼容的 connector/j 版本(例如,8.0.x 系列通常兼容 mysql 8.0+)。
  • 下载platform independent的 zip 文件或 jar 文件。

获取 jar 文件:解压下载的 zip 文件,找到mysql-connector-java-x.x.x.jar文件(x.x.x是版本号,如8.0.33)。

将 jar 文件添加到项目

传统项目:将 jar 文件复制到项目的lib目录下,并在 ide(如 intellij idea, eclipse)中将其添加为项目的库(library)。

maven 项目:在pom.xml中添加依赖:

<dependency>
    <groupid>mysql</groupid>
    <artifactid>mysql-connector-java</artifactid>
    <version>8.0.33</version> <!-- 使用最新稳定版本 -->
</dependency>

gradle 项目:在build.gradle中添加:

implementation 'mysql:mysql-connector-java:8.0.33'

4.数据库和表准备

启动 mysql 服务。

使用 mysql 客户端(如命令行mysql、mysql workbench)创建一个数据库和一张表。例如:

-- 创建数据库
create database if not exists demo_db character set utf8mb4 collate utf8mb4_unicode_ci;

-- 使用数据库
use demo_db;

-- 创建用户表
create table if not exists users (
    id int auto_increment primary key,
    name varchar(100) not null,
    email varchar(150) unique not null,
    created_at timestamp default current_timestamp
);

-- 插入测试数据
insert into users (name, email) values
('张三', 'zhangsan@example.com'),
('李四', 'lisi@example.com'),
('王五', 'wangwu@example.com');

记下数据库名demo_db,表名users,以及你用于连接的用户名(如root)和密码。

二、jdbc 核心组件与连接流程

jdbc 的核心组件主要包括:

  • drivermanager:管理一组 jdbc 驱动程序的基本服务。它是获取connection对象的入口点。
  • connection:代表与特定数据库的连接(会话)。在连接上下文中执行 sql 语句并返回结果。
  • statement / preparedstatement:用于执行静态 sql 语句并返回结果的对象。preparedstatement预编译 sql,能防止 sql 注入,性能更好。
  • resultset:表示数据库查询结果集。通过游标遍历结果。
  • sqlexception:在数据库访问期间发生错误时抛出的异常。

jdbc 标准连接流程

  • 加载数据库驱动(可选,现代 jdbc 通常自动加载)。
  • 建立数据库连接 (connection)。
  • 创建执行 sql 的对象 (statementpreparedstatement)。
  • 执行 sql 语句
  • 处理结果集 (resultset)。
  • 关闭资源(按resultset -> statement -> connection的顺序)。

三、手写 jdbc 代码实现

我们将创建一个简单的 java 程序,完成以下操作:

  • 连接到 mysql 数据库。
  • 查询users表中的所有记录。
  • 打印查询结果。
  • 插入一条新记录。
  • 再次查询并打印结果。

步骤 1:创建 java 项目和类

创建一个名为jdbcdemo的 java 类。

步骤 2:编写代码

import java.sql.*;

/**
 * 手写jdbc连接数据库示例
 * 演示连接mysql、查询、插入操作
 */
public class jdbcdemo {

    // 数据库连接信息 - 请根据实际情况修改
    private static final string db_url = "jdbc:mysql://localhost:3306/demo_db?usessl=false&servertimezone=utc&useunicode=true&characterencoding=utf8";
    private static final string user = "your_username"; // 替换为你的mysql用户名
    private static final string password = "your_password"; // 替换为你的mysql密码

    // jdbc驱动类名 (mysql 8.0+)
    private static final string driver_class = "com.mysql.cj.jdbc.driver";

    public static void main(string[] args) {
        connection connection = null;
        statement statement = null;
        resultset resultset = null;
        preparedstatement preparedstatement = null;

        try {
            // ========== 步骤1:加载jdbc驱动 (可选,现代jdbc通常自动加载) ==========
            // 显式加载驱动类,确保驱动被注册到drivermanager
            // 对于jdbc 4.0+ (java 6+),通常不需要这一步,驱动会自动发现和加载
            // 但显式加载可以确保兼容性或在特定环境下工作
            class.forname(driver_class);
            system.out.println("jdbc驱动加载成功!");

            // ========== 步骤2:建立数据库连接 ==========
            system.out.println("正在连接数据库...");
            connection = drivermanager.getconnection(db_url, user, password);
            system.out.println("数据库连接成功!");

            // ========== 步骤3:创建statement对象 ==========
            statement = connection.createstatement();

            // ========== 步骤4:执行查询sql并处理结果 ==========
            system.out.println("\n--- 查询所有用户信息 ---");
            string selectsql = "select id, name, email, created_at from users";
            resultset = statement.executequery(selectsql); // executequery用于select

            // 处理resultset
            system.out.printf("%-5s %-15s %-25s %-20s%n", "id", "姓名", "邮箱", "创建时间");
            system.out.println("------------------------------------------------------------");
            while (resultset.next()) {
                int id = resultset.getint("id"); // 根据列名获取int值
                string name = resultset.getstring("name"); // 根据列名获取string值
                string email = resultset.getstring("email");
                timestamp createdat = resultset.gettimestamp("created_at");

                // 格式化输出
                system.out.printf("%-5d %-15s %-25s %-20s%n", id, name, email, createdat);
            }

            // ========== 步骤5:使用preparedstatement执行插入操作 (防止sql注入) ==========
            system.out.println("\n--- 准备插入新用户 ---");
            string insertsql = "insert into users (name, email) values (?, ?)";
            preparedstatement = connection.preparestatement(insertsql); // 使用preparedstatement

            // 设置参数值 (索引从1开始)
            preparedstatement.setstring(1, "赵六");
            preparedstatement.setstring(2, "zhaoliu@example.com");

            // 执行插入
            int rowsaffected = preparedstatement.executeupdate(); // executeupdate用于insert, update, delete
            system.out.println("插入操作完成,影响了 " + rowsaffected + " 行。");

            // ========== 步骤6:再次查询,验证插入结果 ==========
            system.out.println("\n--- 再次查询所有用户信息 (验证插入) ---");
            // 重新执行查询
            resultset.close(); // 关闭之前的resultset
            resultset = statement.executequery(selectsql);

            system.out.printf("%-5s %-15s %-25s %-20s%n", "id", "姓名", "邮箱", "创建时间");
            system.out.println("------------------------------------------------------------");
            while (resultset.next()) {
                int id = resultset.getint("id");
                string name = resultset.getstring("name");
                string email = resultset.getstring("email");
                timestamp createdat = resultset.gettimestamp("created_at");
                system.out.printf("%-5d %-15s %-25s %-20s%n", id, name, email, createdat);
            }

        } catch (classnotfoundexception e) {
            system.err.println("jdbc驱动类未找到!请检查驱动jar是否在类路径中。");
            e.printstacktrace();
        } catch (sqlexception e) {
            system.err.println("数据库操作发生错误!");
            e.printstacktrace();
        } finally {
            // ========== 步骤7:关闭资源 (非常重要!) ==========
            // 必须在finally块中关闭,确保即使发生异常也能释放资源
            // 关闭顺序:resultset -> statement/preparedstatement -> connection
            try {
                if (resultset != null) {
                    resultset.close();
                    system.out.println("resultset已关闭。");
                }
            } catch (sqlexception e) {
                system.err.println("关闭resultset时出错!");
                e.printstacktrace();
            }

            try {
                if (statement != null) {
                    statement.close();
                    system.out.println("statement已关闭。");
                }
            } catch (sqlexception e) {
                system.err.println("关闭statement时出错!");
                e.printstacktrace();
            }

            try {
                if (preparedstatement != null) {
                    preparedstatement.close();
                    system.out.println("preparedstatement已关闭。");
                }
            } catch (sqlexception e) {
                system.err.println("关闭preparedstatement时出错!");
                e.printstacktrace();
            }

            try {
                if (connection != null) {
                    connection.close();
                    system.out.println("数据库连接已关闭。");
                }
            } catch (sqlexception e) {
                system.err.println("关闭connection时出错!");
                e.printstacktrace();
            }
        }

        system.out.println("jdbc演示程序结束。");
    }
}

四、代码详解与关键点

1.数据库 url (db_url)

  • jdbc:mysql://:协议前缀,表示使用 mysql 的 jdbc 驱动。
  • localhost:3306:数据库服务器地址和端口。
  • /demo_db:要连接的数据库名称。
  • ?usessl=false&servertimezone=utc&useunicode=true&characterencoding=utf8连接参数,非常重要!
    • usessl=false:在开发环境中常设为false以避免 ssl 证书问题(生产环境应配置 ssl)。
    • servertimezone=utc:设置服务器时区,避免时区不一致导致的时间问题。根据你的服务器时区调整(如asia/shanghai)。
    • useunicode=true&characterencoding=utf8:确保正确处理中文等 unicode 字符。

2.class.forname(driver_class)

  • 这行代码通过反射加载指定的驱动类。加载时,驱动类的静态初始化块会向drivermanager注册自身。
  • 现代 jdbc(jdbc 4.0+) 支持spi(service provider interface) 自动发现机制。只要驱动 jar 在类路径中,drivermanager就能自动找到并加载驱动,因此这行代码通常可以省略。但在某些旧环境或需要确保加载时,保留它是安全的。

3.drivermanager.getconnection()

这是获取connection对象的核心方法。它会遍历已注册的驱动,找到能处理给定 url 的驱动,并建立连接。

4.statement vs preparedstatement

  • statement:用于执行不带参数的静态 sql。直接拼接 sql 字符串,有 sql 注入风险
  • preparedstatement:预编译 sql 语句,使用?作为占位符。通过setxxx()方法设置参数值,能有效防止 sql 注入,且对于重复执行的 sql 性能更高。强烈推荐使用preparedstatement

5.执行方法

  • executequery(): 用于执行select语句,返回resultset
  • executeupdate(): 用于执行insert, update, delete语句,返回影响的行数(int)。
  • execute(): 通用方法,可以执行任何 sql,但较少用。

6.resultset处理

  • next():将游标移动到下一行,如果还有数据返回true,否则false。通常用while循环遍历。
  • getxxx(columnindex)getxxx(columnlabel):根据列的索引(从 1 开始)或列名获取对应类型的值(如getint, getstring, gettimestamp)。

7.资源关闭 (try-catch-finally)

  • 这是最容易被忽视但极其重要的部分! connection, statement, resultset都是宝贵的资源(数据库连接、网络连接、内存等)。必须显式关闭,否则会导致资源泄漏,最终耗尽数据库连接池或内存。
  • 使用try-catch-finally确保finally块中的关闭代码总是执行
  • 关闭顺序:先关闭resultset,再关闭statement/preparedstatement,最后关闭connection。因为resultsetstatement依赖于connection
  • 每个关闭操作都用独立的try-catch:防止一个关闭失败导致后续的关闭无法执行。

五、运行与测试

将代码中的userpassword替换为你实际的 mysql 用户名和密码。

确保mysql-connector-java-x.x.x.jar已正确添加到项目的类路径。

编译并运行jdbcdemo类。

观察控制台输出,应该能看到连接成功、查询结果、插入成功以及再次查询的结果。

六、常见问题与注意事项

classnotfoundexception: 找不到驱动类。检查:

  • driver_class字符串是否正确(mysql 8.0+是com.mysql.cj.jdbc.driver,5.x 是com.mysql.jdbc.driver)。
  • 驱动 jar 文件是否在类路径中?是否被正确添加到项目?

sqlexception: 数据库连接失败。检查:

  • 数据库服务是否启动?
  • db_url中的主机、端口、数据库名是否正确?
  • 用户名和密码是否正确?
  • 连接参数(特别是usessl, servertimezone)是否设置正确?
  • 防火墙是否阻止了端口?

中文乱码: 确保数据库、表、字段的字符集是utf8mb4,并且 jdbc url 中包含useunicode=true&characterencoding=utf8

资源泄漏: 务必在finally块中关闭所有 jdbc 资源。

sql 注入: 永远不要使用statement拼接用户输入的字符串。始终使用preparedstatement

性能: 对于频繁执行的相同 sql,使用preparedstatement可以预编译,提高效率。

七、总结

通过本文,我们从零开始手写了一个完整的 jdbc 程序,涵盖了驱动加载、连接建立、sql 执行(查询和插入)、结果处理以及至关重要的资源管理。虽然现代开发更多依赖于高级框架,但掌握 jdbc 的底层机制,能让你更深刻地理解数据库交互的原理,有助于排查问题、优化性能,并在需要时进行底层定制。

记住核心要点:加载驱动(可选) -> 获取连接 -> 创建语句 -> 执行 sql -> 处理结果 -> 关闭资源(务必!)。

以上就是从零开始手写jdbc连接数据库的详细指南的详细内容,更多关于jdbc连接数据库的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com