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

Java 封裝 MySQL JDBC 工具類 JdbcUtils 完整實現:增刪改查、反射映射 JavaBean、連接池優化

     編輯:MySQL 綜合教學

設計目標

  • 封裝 JDBC 模板代碼 (加載驅動、獲取連接、關閉資源)
  • 統一異常處理 (轉換為運行時異常或統一棄查異常)
  • 支持 **預處理語句** 防注入
  • 提供 **通用查詢** (List<Map>) 與 **反射自動映射** (List<T>) 兩種模式
  • **線程不安全**:每次操作建議 `new JdbcUtils()` 或從連接池獲取連接,不要共享實例。

核心代碼:JdbcUtils.java

package com.jdbc.dbutils;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.*;

public class JdbcUtils {
    // 配置常量 (生產環境應外部化到配置文件/環境變量)
    private static final String DRIVER = "com.mysql.cj.jdbc.Driver"; // MySQL 8.0+
    private static final String URL = "jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC&characterEncoding=utf8";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "your_password";

    private Connection conn;
    private PreparedStatement pstmt;
    private ResultSet rs;

    public JdbcUtils() {
        try {
            Class.forName(DRIVER);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("JDBC驅動加載失敗", e);
        }
    }

    /** 獲取連接 (建議改為從 DataSource 獲取) */
    public Connection getConnection() throws SQLException {
        if (conn == null || conn.isClosed()) {
            conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
        }
        return conn;
    }

    /** 通用增刪改 (INSERT/UPDATE/DELETE) */
    public boolean update(String sql, List<Object> params) throws SQLException {
        pstmt = getConnection().prepareStatement(sql);
        bindParams(params);
        int rows = pstmt.executeUpdate();
        return rows > 0;
    }

    /** 查詢單條 -> Map */
    public Map<String, Object> queryForMap(String sql, List<Object> params) throws SQLException {
        List<Map<String, Object>> list = queryForList(sql, params);
        return list.isEmpty() ? null : list.get(0);
    }

    /** 查詢多條 -> List<Map> */
    public List<Map<String, Object>> queryForList(String sql, List<Object> params) throws SQLException {
        pstmt = getConnection().prepareStatement(sql);
        bindParams(params);
        rs = pstmt.executeQuery();
        return mapResultSet(rs);
    }

    /** 反射查詢單條 -> JavaBean */
    public <T> T queryForObject(String sql, List<Object> params, Class<T> clazz) throws Exception {
        List<T> list = queryForList(sql, params, clazz);
        return list.isEmpty() ? null : list.get(0);
    }

    /** 反射查詢多條 -> List<JavaBean> */
    public <T> List<T> queryForList(String sql, List<Object> params, Class<T> clazz) throws Exception {
        pstmt = getConnection().prepareStatement(sql);
        bindParams(params);
        rs = pstmt.executeQuery();
        return mapResultSetToBean(rs, clazz);
    }

    /** 綁定參數 */
    private void bindParams(List<Object> params) throws SQLException {
        if (params != null) {
            for (int i = 0; i < params.size(); i++) {
                pstmt.setObject(i + 1, params.get(i));
            }
        }
    }

    /** ResultSet -> List<Map> */
    private List<Map<String, Object>> mapResultSet(ResultSet rs) throws SQLException {
        List<Map<String, Object>> list = new ArrayList<>();
        ResultSetMetaData meta = rs.getMetaData();
        int colCount = meta.getColumnCount();
        while (rs.next()) {
            Map<String, Object> row = new HashMap<>();
            for (int i = 1; i <= colCount; i++) {
                String colName = meta.getColumnLabel(i); // 別名優先
                Object val = rs.getObject(i);
                row.put(colName, val);
            }
            list.add(row);
        }
        return list;
    }

    /** ResultSet -> List<JavaBean> (反射核心) */
    private <T> List<T> mapResultSetToBean(ResultSet rs, Class<T> clazz) throws Exception {
        List<T> list = new ArrayList<>();
        ResultSetMetaData meta = rs.getMetaData();
        int colCount = meta.getColumnCount();
        while (rs.next()) {
            T obj = clazz.getDeclaredConstructor().newInstance(); // 需無參構造器
            for (int i = 1; i <= colCount; i++) {
                String colName = meta.getColumnLabel(i);
                Object val = rs.getObject(i);
                if (val == null) continue;
                try {
                    Field field = clazz.getDeclaredField(colName);
                    field.setAccessible(true); // 破解 private
                    // 類型轉換處理 (簡化版,生產需完善)
                    field.set(obj, convertValue(val, field.getType()));
                } catch (NoSuchFieldException ignored) {
                    // 列名與字段名不匹配時忽略 (或拋出異常)
                }
            }
            list.add(obj);
        }
        return list;
    }

    /** 簡單類型轉換 (擴展點) */
    private Object convertValue(Object val, Class<?> targetType) {
        if (targetType.isInstance(val)) return val;
        if (targetType == Integer.class || targetType == int.class) return ((Number) val).intValue();
        if (targetType == Long.class || targetType == long.class) return ((Number) val).longValue();
        if (targetType == String.class) return val.toString();
        if (targetType == Boolean.class || targetType == boolean.class) return ((Number) val).intValue() != 0;
        if (targetType == java.util.Date.class && val instanceof java.sql.Date) return new java.util.Date(((java.sql.Date) val).getTime());
        // ... 更多類型
        return val;
    }

    /** 釋放資源 (按逆序關閉) */
    public void close() {
        try { if (rs != null) rs.close(); } catch (SQLException e) { e.printStackTrace(); }
        try { if (pstmt != null) pstmt.close(); } catch (SQLException e) { e.printStackTrace(); }
        try { if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); }
    }

    // ========== 測試主方法 ==========
    public static void main(String[] args) {
        JdbcUtils db = new JdbcUtils();
        try {
            // 增
            db.update("INSERT INTO userinfo(username, pswd) VALUES(?,?)", Arrays.asList("張三", "123456"));
            // 查 Map
            List<Map<String, Object>> list = db.queryForList("SELECT * FROM userinfo", null);
            System.out.println("Map模式: " + list);
            // 查 Bean
            List<UserInfo> users = db.queryForList("SELECT * FROM userinfo", null, UserInfo.class);
            System.out.println("Bean模式: " + users);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            db.close();
        }
    }
}

JavaBean 示例:UserInfo.java

package domain;

public class UserInfo {
    private int id;
    private String username;
    private String pswd;

    // 必須有無參構造器
    public UserInfo() {}

    // Getter/Setter (反射調用 setXxx)
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    public String getPswd() { return pswd; }
    public void setPswd(String pswd) { this.pswd = pswd; }

    @Override public String toString() {
        return "UserInfo{id=" + id + ", username='" + username + "', pswd='" + pswd + "'}";
    }
}

表結構 (Navicat 建表)

CREATE TABLE `userinfo` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL,
  `pswd` varchar(100) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

關鍵技術點解析

1. 反射映射流程

  1. clazz.getDeclaredConstructor().newInstance() 實例化 Bean (要求無參構造器)。
  2. rs.getMetaData().getColumnLabel(i) 獲取列名/別名。
  3. clazz.getDeclaredField(colName) 獲取字段 (含 private)。
  4. field.setAccessible(true) 破解訪問權限。
  5. field.set(obj, value) 賦值,**自動調用 Setter** (若存在) 或直接寫字段。

2. JDBC 標準操作 7 步驟

  1. 加載驅動:Class.forName(DRIVER) (觸發靜態塊註冊 Driver)
  2. 建立連接:DriverManager.getConnection(URL, USER, PASS)
  3. 創建 Statement:conn.prepareStatement(sql) (預處理)
  4. 綁定參數:pstmt.setObject(index, val)
  5. 執行:executeUpdate() / executeQuery()
  6. 處理結果:遍歷 ResultSet、映射對象
  7. 關閉資源:ResultSet → Statement → Connection (逆序,finally 塊保證)

3. 生產環境必做優化

問題解決方案
頻繁創建連接開銷大引入 **連接池** (HikariCP / Druid / DBCP2),DataSource.getConnection() 替代 DriverManager
工具類線程不安全**每請求一實例** 或 **ThreadLocal 綁定連接** (Spring JdbcTemplate 模式)
異常處理粗糙自定義 DaoException 包裝 SQLException,保留堆棧,上層統一處理
類型轉換不全完善 convertValue 支持 BigDecimal, LocalDateTime, Enum, JSON 等
SQL 注入風險**堅決禁止字符串拼接 SQL**,全量使用 ? 佔位符 + setObject
事務管理缺失添加 beginTransaction(), commit(), rollback() 方法,配合 ThreadLocal 管理事務邊界

Maven 依賴 (MySQL 8.0+)

<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>8.0.33</version>
</dependency>
<!-- 連接池推薦 HikariCP -->
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>5.0.1</version>
</dependency>

總結

JdbcUtils 展示了 JDBC 核心 API 與 Java 反射結合的經典模式。理解其原理有助於深入掌握 Spring JdbcTemplate、MyBatis、Hibernate 等 ORM 框架的底層實現。**生產項目請直接使用成熟框架**,勿重複造輪子。

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