首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java JDBC 連接 MySQL 底層實現全解析:驅動註冊、連接獲取、CRUD 封裝與工具類設計

     編輯:Java 綜合教學

JDBC 連接 MySQL 底層實現全解析

雖然生產多用連接池(HikariCP/Druid)+ ORM(MyBatis/JPA),但理解 JDBC 原生流程對排查連接洩漏、事務邊界、SQL 注入等問題至關重要。本文從驅動加載到工具類封裝,還原最核心路徑。

1. 核心四步驟


// 1. 註冊驅動(JDBC 4.0+ 可省,Class.forName 觸發 Driver 靜態塊自動註冊)
Class.forName("com.mysql.jdbc.Driver");  // 5.1.x
// Class.forName("com.mysql.cj.jdbc.Driver"); // 8.0+

// 2. 獲取連接
Connection conn = DriverManager.getConnection(
    "jdbc:mysql://localhost:3308/lab?useSSL=false&serverTimezone=UTC",
    "lmapp", "lmapp");

// 3. 預編譯語句(防注入、提升性能)
PreparedStatement ps = conn.prepareStatement(
    "INSERT INTO lab_static_attribute(uid,login_time,out_time,last_time) VALUES(?,?,?,?)");
ps.setInt(1, uid);
ps.setTimestamp(2, loginTime);
ps.setTimestamp(3, outTime);
ps.setLong(4, lastTime);
ps.executeUpdate();

// 4. 釋放資源(反向順序,finally 或 try-with-resources)
ps.close();
conn.close();

2. 常量配置類


public class Const {
    public static final String IP = "localhost";
    public static final String PORT = "3308";
    public static final String DB = "lab";
    public static final String USER = "lmapp";
    public static final String PWD = "lmapp";
    public static final String DRIVER = "com.mysql.jdbc.Driver";
    public static final String URL = "jdbc:mysql://" + IP + ":" + PORT + "/" + DB
        + "?useSSL=false&serverTimezone=UTC&characterEncoding=UTF-8";
}

3. 通用工具類 MysqlUtil(線程不安全,僅教學用)


public class MysqlUtil {
    // 單例連接(生產禁用,必須用連接池)
    private static Connection conn;

    public static Connection getConnection() throws SQLException {
        if (conn == null || conn.isClosed()) {
            conn = DriverManager.getConnection(Const.URL, Const.USER, Const.PWD);
        }
        return conn;
    }

    // 增刪改 通用
    public static boolean executeUpdate(String sql, List<Object> params) {
        try (PreparedStatement ps = getConnection().prepareStatement(sql)) {
            setParams(ps, params);
            return ps.executeUpdate() > 0;
        } catch (SQLException e) {
            e.printStackTrace();
            return false;
        }
    }

    // 查詢單條 -> Map
    public static Map<String, Object> queryOne(String sql, List<Object> params) {
        try (PreparedStatement ps = getConnection().prepareStatement(sql);
             ResultSet rs = ps.executeQuery()) {
            setParams(ps, params);
            if (rs.next()) return resultSetToMap(rs);
        } catch (SQLException e) { e.printStackTrace(); }
        return null;
    }

    // 查詢多條 -> List<Map>
    public static List<Map<String, Object>> queryList(String sql, List<Object> params) {
        List<Map<String, Object>> list = new ArrayList<>();
        try (PreparedStatement ps = getConnection().prepareStatement(sql);
             ResultSet rs = ps.executeQuery()) {
            setParams(ps, params);
            while (rs.next()) list.add(resultSetToMap(rs));
        } catch (SQLException e) { e.printStackTrace(); }
        return list;
    }

    private static void setParams(PreparedStatement ps, List<Object> params) throws SQLException {
        if (params != null) {
            for (int i = 0; i < params.size(); i++) ps.setObject(i + 1, params.get(i));
        }
    }

    private static Map<String, Object> resultSetToMap(ResultSet rs) throws SQLException {
        ResultSetMetaData md = rs.getMetaData();
        int cols = md.getColumnCount();
        Map<String, Object> map = new LinkedHashMap<>();
        for (int i = 1; i <= cols; i++) map.put(md.getColumnLabel(i), rs.getObject(i));
        return map;
    }
}

4. DAO 層調用示例


public class UserDao {
    public int add(User u) {
        String sql = "INSERT INTO lab_static_attribute(uid,login_time,out_time,last_time) VALUES(?,?,?,?)";
        List<Object> params = Arrays.asList(u.getUid(), u.getLoginTime(), u.getOutTime(), u.getLastTime());
        return MysqlUtil.executeUpdate(sql, params) ? 1 : 0;
    }
}

5. 核心注意事項

項目說明
驅動版本匹配MySQL 5.x 用 5.1.x,8.x 用 8.0+,類名不同
URL 參數useSSL=false、serverTimezone=UTC、characterEncoding=UTF-8、allowPublicKeyRetrieval=true (8.0)
預編譯必須用 PreparedStatement,防 SQL 注入、複用執行計劃
資源關閉Java 7+ try-with-resources 自動關閉 ResultSet/Statement/Connection
事務控制conn.setAutoCommit(false) → 操作 → commit()/rollback()
連接池生產必須用 HikariCP/Druid,配置最大/最小連接、超時、驗證查詢
異常處理捕獲 SQLException 後轉換為業務異常,保留堆棧

6. 進階:ResultSet 操作技巧

  • rs.getMetaData() 獲取列名/類型,實現通用 Map/Bean 映射
  • rs.wasNull() 判斷數據庫 NULL(基本類型 getInt 返回 0 無法區分)
  • rs.getObject(columnIndex) 通用取值,配合類型轉換
  • 大結果集:stmt.setFetchSize(Integer.MIN_VALUE) (MySQL) 流式讀取,避免 OOM

掌握 JDBC 底層細節,能在 ORM 出問題時「看穿本質」,也是面試高頻考點的硬核加分項。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved