最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

從零開始手寫JDBC連接數(shù)據(jù)庫的詳細指南

 更新時間:2025年07月30日 10:25:50   作者:超級小忍  
在?Java?開發(fā)中,數(shù)據(jù)庫是存儲和管理數(shù)據(jù)的核心組件,JDBC是?Java?程序與數(shù)據(jù)庫交互的標準?API,本文將帶大家手寫一個完整的?JDBC?連接數(shù)據(jù)庫的示例,希望對大家有所幫助

前言

在 Java 開發(fā)中,數(shù)據(jù)庫是存儲和管理數(shù)據(jù)的核心組件。而 JDBC(Java Database Connectivity)是 Java 程序與數(shù)據(jù)庫交互的標準 API,它允許 Java 應(yīng)用程序連接各種關(guān)系型數(shù)據(jù)庫(如 MySQL、Oracle、PostgreSQL 等),并執(zhí)行 SQL 語句。雖然現(xiàn)代開發(fā)中常使用 ORM 框架(如 MyBatis、Hibernate)簡化數(shù)據(jù)庫操作,但理解 JDBC 的底層原理對于深入掌握數(shù)據(jù)庫編程至關(guān)重要。

本文將帶你手寫一個完整的 JDBC 連接數(shù)據(jù)庫的示例,涵蓋從環(huán)境準備到代碼實現(xiàn)的每一個細節(jié),力求做到“越詳細越好”。

一、環(huán)境準備

在開始編碼之前,確保你的開發(fā)環(huán)境已準備好以下工具和資源:

1.JDK(Java Development Kit)

  • 確保已安裝 JDK 8 或更高版本。JDBC 是 Java SE 的一部分,因此 JDK 自帶 JDBC API。
  • 檢查方法:在命令行輸入 java -versionjavac -version。

2.數(shù)據(jù)庫

  • 本文以MySQL為例(版本 8.0+)。你需要安裝并啟動 MySQL 服務(wù)。
  • 確保你知道 MySQL 的主機地址(通常是localhost127.0.0.1)、端口號(默認3306)、數(shù)據(jù)庫名稱、用戶名密碼

3.數(shù)據(jù)庫驅(qū)動(JDBC Driver)

JDBC 需要特定數(shù)據(jù)庫的驅(qū)動程序來實現(xiàn)與數(shù)據(jù)庫的通信。對于 MySQL,需要mysql-connector-java驅(qū)動。

下載驅(qū)動

  • 訪問 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 文件添加到項目

傳統(tǒng)項目:將 JAR 文件復(fù)制到項目的lib目錄下,并在 IDE(如 IntelliJ IDEA, Eclipse)中將其添加為項目的庫(Library)。

Maven 項目:在pom.xml中添加依賴:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.33</version> <!-- 使用最新穩(wěn)定版本 -->
</dependency>

Gradle 項目:在build.gradle中添加:

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

4.數(shù)據(jù)庫和表準備

啟動 MySQL 服務(wù)。

使用 MySQL 客戶端(如命令行mysql、MySQL Workbench)創(chuàng)建一個數(shù)據(jù)庫和一張表。例如:

-- 創(chuàng)建數(shù)據(jù)庫
CREATE DATABASE IF NOT EXISTS demo_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 使用數(shù)據(jù)庫
USE demo_db;

-- 創(chuàng)建用戶表
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
);

-- 插入測試數(shù)據(jù)
INSERT INTO users (name, email) VALUES
('張三', 'zhangsan@example.com'),
('李四', 'lisi@example.com'),
('王五', 'wangwu@example.com');

記下數(shù)據(jù)庫名demo_db,表名users,以及你用于連接的用戶名(如root)和密碼。

二、JDBC 核心組件與連接流程

JDBC 的核心組件主要包括:

  • DriverManager:管理一組 JDBC 驅(qū)動程序的基本服務(wù)。它是獲取Connection對象的入口點。
  • Connection:代表與特定數(shù)據(jù)庫的連接(會話)。在連接上下文中執(zhí)行 SQL 語句并返回結(jié)果。
  • Statement / PreparedStatement:用于執(zhí)行靜態(tài) SQL 語句并返回結(jié)果的對象。PreparedStatement預(yù)編譯 SQL,能防止 SQL 注入,性能更好。
  • ResultSet:表示數(shù)據(jù)庫查詢結(jié)果集。通過游標遍歷結(jié)果。
  • SQLException:在數(shù)據(jù)庫訪問期間發(fā)生錯誤時拋出的異常。

JDBC 標準連接流程

  • 加載數(shù)據(jù)庫驅(qū)動(可選,現(xiàn)代 JDBC 通常自動加載)。
  • 建立數(shù)據(jù)庫連接 (Connection)。
  • 創(chuàng)建執(zhí)行 SQL 的對象 (StatementPreparedStatement)。
  • 執(zhí)行 SQL 語句。
  • 處理結(jié)果集 (ResultSet)。
  • 關(guān)閉資源(按ResultSet -> Statement -> Connection的順序)。

三、手寫 JDBC 代碼實現(xiàn)

我們將創(chuàng)建一個簡單的 Java 程序,完成以下操作:

  • 連接到 MySQL 數(shù)據(jù)庫。
  • 查詢users表中的所有記錄。
  • 打印查詢結(jié)果。
  • 插入一條新記錄。
  • 再次查詢并打印結(jié)果。

步驟 1:創(chuàng)建 Java 項目和類

創(chuàng)建一個名為JDBCDemo的 Java 類。

步驟 2:編寫代碼

import java.sql.*;

/**
 * 手寫JDBC連接數(shù)據(jù)庫示例
 * 演示連接MySQL、查詢、插入操作
 */
public class JDBCDemo {

    // 數(shù)據(jù)庫連接信息 - 請根據(jù)實際情況修改
    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驅(qū)動類名 (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驅(qū)動 (可選,現(xiàn)代JDBC通常自動加載) ==========
            // 顯式加載驅(qū)動類,確保驅(qū)動被注冊到DriverManager
            // 對于JDBC 4.0+ (Java 6+),通常不需要這一步,驅(qū)動會自動發(fā)現(xiàn)和加載
            // 但顯式加載可以確保兼容性或在特定環(huán)境下工作
            Class.forName(DRIVER_CLASS);
            System.out.println("JDBC驅(qū)動加載成功!");

            // ========== 步驟2:建立數(shù)據(jù)庫連接 ==========
            System.out.println("正在連接數(shù)據(jù)庫...");
            connection = DriverManager.getConnection(DB_URL, USER, PASSWORD);
            System.out.println("數(shù)據(jù)庫連接成功!");

            // ========== 步驟3:創(chuàng)建Statement對象 ==========
            statement = connection.createStatement();

            // ========== 步驟4:執(zhí)行查詢SQL并處理結(jié)果 ==========
            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", "姓名", "郵箱", "創(chuàng)建時間");
            System.out.println("------------------------------------------------------------");
            while (resultSet.next()) {
                int id = resultSet.getInt("id"); // 根據(jù)列名獲取int值
                String name = resultSet.getString("name"); // 根據(jù)列名獲取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執(zhí)行插入操作 (防止SQL注入) ==========
            System.out.println("\n--- 準備插入新用戶 ---");
            String insertSql = "INSERT INTO users (name, email) VALUES (?, ?)";
            preparedStatement = connection.prepareStatement(insertSql); // 使用PreparedStatement

            // 設(shè)置參數(shù)值 (索引從1開始)
            preparedStatement.setString(1, "趙六");
            preparedStatement.setString(2, "zhaoliu@example.com");

            // 執(zhí)行插入
            int rowsAffected = preparedStatement.executeUpdate(); // executeUpdate用于INSERT, UPDATE, DELETE
            System.out.println("插入操作完成,影響了 " + rowsAffected + " 行。");

            // ========== 步驟6:再次查詢,驗證插入結(jié)果 ==========
            System.out.println("\n--- 再次查詢所有用戶信息 (驗證插入) ---");
            // 重新執(zhí)行查詢
            resultSet.close(); // 關(guān)閉之前的ResultSet
            resultSet = statement.executeQuery(selectSql);

            System.out.printf("%-5s %-15s %-25s %-20s%n", "ID", "姓名", "郵箱", "創(chuàng)建時間");
            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驅(qū)動類未找到!請檢查驅(qū)動JAR是否在類路徑中。");
            e.printStackTrace();
        } catch (SQLException e) {
            System.err.println("數(shù)據(jù)庫操作發(fā)生錯誤!");
            e.printStackTrace();
        } finally {
            // ========== 步驟7:關(guān)閉資源 (非常重要!) ==========
            // 必須在finally塊中關(guān)閉,確保即使發(fā)生異常也能釋放資源
            // 關(guān)閉順序:ResultSet -> Statement/PreparedStatement -> Connection
            try {
                if (resultSet != null) {
                    resultSet.close();
                    System.out.println("ResultSet已關(guān)閉。");
                }
            } catch (SQLException e) {
                System.err.println("關(guān)閉ResultSet時出錯!");
                e.printStackTrace();
            }

            try {
                if (statement != null) {
                    statement.close();
                    System.out.println("Statement已關(guān)閉。");
                }
            } catch (SQLException e) {
                System.err.println("關(guān)閉Statement時出錯!");
                e.printStackTrace();
            }

            try {
                if (preparedStatement != null) {
                    preparedStatement.close();
                    System.out.println("PreparedStatement已關(guān)閉。");
                }
            } catch (SQLException e) {
                System.err.println("關(guān)閉PreparedStatement時出錯!");
                e.printStackTrace();
            }

            try {
                if (connection != null) {
                    connection.close();
                    System.out.println("數(shù)據(jù)庫連接已關(guān)閉。");
                }
            } catch (SQLException e) {
                System.err.println("關(guān)閉Connection時出錯!");
                e.printStackTrace();
            }
        }

        System.out.println("JDBC演示程序結(jié)束。");
    }
}

四、代碼詳解與關(guān)鍵點

1.數(shù)據(jù)庫 URL (DB_URL)

  • jdbc:mysql://:協(xié)議前綴,表示使用 MySQL 的 JDBC 驅(qū)動。
  • localhost:3306:數(shù)據(jù)庫服務(wù)器地址和端口。
  • /demo_db:要連接的數(shù)據(jù)庫名稱。
  • ?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf8連接參數(shù),非常重要!
    • useSSL=false:在開發(fā)環(huán)境中常設(shè)為false以避免 SSL 證書問題(生產(chǎn)環(huán)境應(yīng)配置 SSL)。
    • serverTimezone=UTC:設(shè)置服務(wù)器時區(qū),避免時區(qū)不一致導(dǎo)致的時間問題。根據(jù)你的服務(wù)器時區(qū)調(diào)整(如Asia/Shanghai)。
    • useUnicode=true&characterEncoding=utf8:確保正確處理中文等 Unicode 字符。

2.Class.forName(DRIVER_CLASS)

  • 這行代碼通過反射加載指定的驅(qū)動類。加載時,驅(qū)動類的靜態(tài)初始化塊會向DriverManager注冊自身。
  • 現(xiàn)代 JDBC(JDBC 4.0+) 支持SPI(Service Provider Interface) 自動發(fā)現(xiàn)機制。只要驅(qū)動 JAR 在類路徑中,DriverManager就能自動找到并加載驅(qū)動,因此這行代碼通??梢允÷?/strong>。但在某些舊環(huán)境或需要確保加載時,保留它是安全的。

3.DriverManager.getConnection()

這是獲取Connection對象的核心方法。它會遍歷已注冊的驅(qū)動,找到能處理給定 URL 的驅(qū)動,并建立連接。

4.Statement vs PreparedStatement

  • Statement:用于執(zhí)行不帶參數(shù)的靜態(tài) SQL。直接拼接 SQL 字符串,有 SQL 注入風險。
  • PreparedStatement:預(yù)編譯 SQL 語句,使用?作為占位符。通過setXXX()方法設(shè)置參數(shù)值,能有效防止 SQL 注入,且對于重復(fù)執(zhí)行的 SQL 性能更高。強烈推薦使用PreparedStatement。

5.執(zhí)行方法

  • executeQuery(): 用于執(zhí)行SELECT語句,返回ResultSet。
  • executeUpdate(): 用于執(zhí)行INSERT, UPDATE, DELETE語句,返回影響的行數(shù)(int)。
  • execute(): 通用方法,可以執(zhí)行任何 SQL,但較少用。

6.ResultSet處理

  • next():將游標移動到下一行,如果還有數(shù)據(jù)返回true,否則false。通常用while循環(huán)遍歷。
  • getXXX(columnIndex)getXXX(columnLabel):根據(jù)列的索引(從 1 開始)或列名獲取對應(yīng)類型的值(如getInt, getString, getTimestamp)。

7.資源關(guān)閉 (try-catch-finally)

  • 這是最容易被忽視但極其重要的部分! Connection, Statement, ResultSet都是寶貴的資源(數(shù)據(jù)庫連接、網(wǎng)絡(luò)連接、內(nèi)存等)。必須顯式關(guān)閉,否則會導(dǎo)致資源泄漏,最終耗盡數(shù)據(jù)庫連接池或內(nèi)存。
  • 使用try-catch-finally確保finally塊中的關(guān)閉代碼總是執(zhí)行
  • 關(guān)閉順序:先關(guān)閉ResultSet,再關(guān)閉Statement/PreparedStatement,最后關(guān)閉Connection。因為ResultSetStatement依賴于Connection
  • 每個關(guān)閉操作都用獨立的try-catch:防止一個關(guān)閉失敗導(dǎo)致后續(xù)的關(guān)閉無法執(zhí)行。

五、運行與測試

將代碼中的USERPASSWORD替換為你實際的 MySQL 用戶名和密碼。

確保mysql-connector-java-x.x.x.jar已正確添加到項目的類路徑。

編譯并運行JDBCDemo類。

觀察控制臺輸出,應(yīng)該能看到連接成功、查詢結(jié)果、插入成功以及再次查詢的結(jié)果。

六、常見問題與注意事項

ClassNotFoundException: 找不到驅(qū)動類。檢查:

  • DRIVER_CLASS字符串是否正確(MySQL 8.0+是com.mysql.cj.jdbc.Driver,5.x 是com.mysql.jdbc.Driver)。
  • 驅(qū)動 JAR 文件是否在類路徑中?是否被正確添加到項目?

SQLException: 數(shù)據(jù)庫連接失敗。檢查:

  • 數(shù)據(jù)庫服務(wù)是否啟動?
  • DB_URL中的主機、端口、數(shù)據(jù)庫名是否正確?
  • 用戶名和密碼是否正確?
  • 連接參數(shù)(特別是useSSL, serverTimezone)是否設(shè)置正確?
  • 防火墻是否阻止了端口?

中文亂碼: 確保數(shù)據(jù)庫、表、字段的字符集是utf8mb4,并且 JDBC URL 中包含useUnicode=true&characterEncoding=utf8。

資源泄漏: 務(wù)必在finally塊中關(guān)閉所有 JDBC 資源。

SQL 注入: 永遠不要使用Statement拼接用戶輸入的字符串。始終使用PreparedStatement。

性能: 對于頻繁執(zhí)行的相同 SQL,使用PreparedStatement可以預(yù)編譯,提高效率。

七、總結(jié)

通過本文,我們從零開始手寫了一個完整的 JDBC 程序,涵蓋了驅(qū)動加載、連接建立、SQL 執(zhí)行(查詢和插入)、結(jié)果處理以及至關(guān)重要的資源管理。雖然現(xiàn)代開發(fā)更多依賴于高級框架,但掌握 JDBC 的底層機制,能讓你更深刻地理解數(shù)據(jù)庫交互的原理,有助于排查問題、優(yōu)化性能,并在需要時進行底層定制。

記住核心要點:加載驅(qū)動(可選) -> 獲取連接 -> 創(chuàng)建語句 -> 執(zhí)行 SQL -> 處理結(jié)果 -> 關(guān)閉資源(務(wù)必?。?。

以上就是從零開始手寫JDBC連接數(shù)據(jù)庫的詳細指南的詳細內(nèi)容,更多關(guān)于JDBC連接數(shù)據(jù)庫的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringBoot2.0集成Swagger2訪問404的解決操作

    SpringBoot2.0集成Swagger2訪問404的解決操作

    這篇文章主要介紹了SpringBoot2.0集成Swagger2訪問404的解決操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • SpringBoot日志配置SLF4J和Logback的方法實現(xiàn)

    SpringBoot日志配置SLF4J和Logback的方法實現(xiàn)

    日志記錄是不可或缺的一部分,本文主要介紹了SpringBoot日志配置SLF4J和Logback的方法實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-04-04
  • Java多線程 樂觀鎖和CAS機制詳細

    Java多線程 樂觀鎖和CAS機制詳細

    這篇文章主要介紹了Java多線程 樂觀鎖和CAS機制,樂觀鎖是對于數(shù)據(jù)沖突保持一種樂觀態(tài)度,操作數(shù)據(jù)時不會對操作的數(shù)據(jù)進行加鎖,需要的朋友可以參考下
    2021-10-10
  • 在SpringBoot項目中整合攔截器的詳細步驟

    在SpringBoot項目中整合攔截器的詳細步驟

    在系統(tǒng)中經(jīng)常需要在處理用戶請求之前和之后執(zhí)行一些行為,例如檢測用戶的權(quán)限,或者將請求的信息記錄到日志中,即平時所說的"權(quán)限檢測"及"日志記錄",下面這篇文章主要給大家介紹了關(guān)于在SpringBoot項目中整合攔截器的相關(guān)資料,需要的朋友可以參考下
    2022-09-09
  • Spring整合Mybatis使用<context:property-placeholder>時的坑

    Spring整合Mybatis使用<context:property-placeholder>時的坑

    這篇文章主要介紹了Spring整合Mybatis使用<context:property-placeholder>時的坑 的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-06-06
  • idea中ctrl+shift+f(在文件中查找)失效問題及解決過程

    idea中ctrl+shift+f(在文件中查找)失效問題及解決過程

    這段描述主要講解了解決Win1微軟鍵盤在Idea中按鍵無效的問題,以及搜狗輸入法簡繁體切換快捷鍵的關(guān)閉方法,幫助用戶優(yōu)化輸入體驗
    2026-05-05
  • 使用SpringMVC返回json字符串的實例講解

    使用SpringMVC返回json字符串的實例講解

    下面小編就為大家分享一篇使用SpringMVC返回json字符串的實例講解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-03-03
  • Java后端用EL表達式改進JSP

    Java后端用EL表達式改進JSP

    EL 全名為Expression Language,EL的語法很簡單,它最大的特點就是使用上很方便,本文帶你用EL表達式改進JSP,感興趣的朋友來看看吧
    2022-02-02
  • SpringBoot獲取配置信息的三種方式總結(jié)

    SpringBoot獲取配置信息的三種方式總結(jié)

    這篇文章給大家介紹了SpringBoot獲取配置信息的三種方式,@Value屬性值注入,綁定配置類和通過 environment獲取這三種方式,文中通過代碼示例給大家介紹的非常詳細,具有一定的參考價值,需要的朋友可以參考下
    2024-01-01
  • Java編程通過list接口實現(xiàn)數(shù)據(jù)的增刪改查代碼示例

    Java編程通過list接口實現(xiàn)數(shù)據(jù)的增刪改查代碼示例

    這篇文章是介紹Java編程基礎(chǔ)方面的內(nèi)容,涉及l(fā)ist接口的操作,通過list接口實現(xiàn)對數(shù)據(jù)的增刪改查的相關(guān)代碼,具有一定參考價值,需要的朋友可以了解下。
    2017-10-10

最新評論

沙河市| 桦甸市| 阳泉市| 巴马| 南丰县| 留坝县| 绥德县| 墨竹工卡县| 长岭县| 安新县| 墨江| 西昌市| 和田市| 靖西县| 阳江市| 兴化市| 平罗县| 兴宁市| 青浦区| 图片| 珠海市| 大庆市| 九寨沟县| 嘉善县| 松潘县| 乌鲁木齐县| 北京市| 修水县| 锦屏县| 彰武县| 丰顺县| 平安县| 阿拉善左旗| 浪卡子县| 丰镇市| 阳西县| 万全县| 集安市| 永新县| 东安县| 临猗县|