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

Java 通用資料庫操作工具類 DBUtil:支援多資料庫分頁、CRUD、反射映射與註解驅動

     編輯:Java 相關知識

設計目標

  • 統一 MySQL / Oracle / PostgreSQL / SQL Server 等異構資料庫的 CRUD 與分頁 API。
  • 基於 PreparedStatement 防注入,支援實體類反射映射與 @Column 註解自定義欄位名。
  • 自動處理資源關閉、異常轉換、計數查詢與分頁 SQL 改寫。

核心架構

public class DBUtil {
    // 單例 DataSource / 連線池(此處示範簡單 DriverManager)
    private static final String URL = "jdbc:postgresql://192.168.56.101/db";
    private static final String USER = "test";
    private static final String PASS = "12345678";
    private static String driverClass; // 連線後動態偵測

    static {
        try { Class.forName("org.postgresql.Driver"); }
        catch (Exception e) { throw new ExceptionInInitializerError(e); }
    }

    // 內部:取得連線並偵測驅動類別
    private static Connection getConnection() throws SQLException {
        Connection conn = DriverManager.getConnection(URL, USER, PASS);
        driverClass = conn.getMetaData().getDriverName(); // 實際為驅動類名
        return conn;
    }

    // 內部:統一關閉資源
    private static void close(Connection c, Statement s, ResultSet r) {
        closeQuietly(r); closeQuietly(s); closeQuietly(c);
    }
    private static void closeQuietly(AutoCloseable c) {
        if (c != null) try { c.close(); } catch (Exception ignored) {}
    }
}

分頁 SQL 自動改寫(核心亮點)

根據偵測到的驅動類別,自動生成對應方言的 LIMIT/OFFSET、ROWNUM、TOP 等分頁語法。

private static String buildPageSql(String sql, int offset, int limit) {
    StringBuilder sb = new StringBuilder();
    if (driverClass.contains("MySQL") || driverClass.contains("PostgreSQL")) {
        sb.append(sql).append(" LIMIT ").append(limit).append(" OFFSET ").append(offset);
    } else if (driverClass.contains("Oracle")) {
        // Oracle 12c+ 可用 OFFSET/FETCH,此處示範傳統 ROWNUM 寫法
        List<String> cols = extractSelectColumns(sql);
        sb.append("SELECT ").append(String.join(", ", cols))
          .append(" FROM (").append(sql).append(") WHERE ROWNUM BETWEEN ")
          .append(offset + 1).append(" AND ").append(offset + limit);
    } else if (driverClass.contains("SQLServer")) {
        // SQL Server 2012+ OFFSET/FETCH
        sb.append(sql).append(" ORDER BY (SELECT 0) OFFSET ").append(offset).append(" ROWS FETCH NEXT ").append(limit).append(" ROWS ONLY");
    } else {
        throw new UnsupportedOperationException("Unsupported dialect: " + driverClass);
    }
    return sb.toString();
}

實體映射:反射 + @Column 註解

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
    String name() default "";
}

// 使用範例
public class User {
    @Column(name = "user_id")   private Long id;
    @Column(name = "user_name") private String name;
    private String email; // 欄位名與屬性名相同,無需註解
    // getter/setter 略
}

核心 CRUD API

// 插入:自動解析 INSERT SQL 欄位順序,反射取值綁定參數
public static <T> int insert(String sql, T entity) { ... }

// 更新:解析 UPDATE SET 及 WHERE 條件欄位
public static <T> int update(String sql, T entity) { ... }

// 刪除/批量更新:參數陣列版
public static int update(String sql, Object... params) { ... }

// 查詢多筆
public static <T> List<T> queryList(Class<T> clazz, String sql, Object... params) { ... }

// 分頁查詢
public static <T> List<T> queryPage(Class<T> clazz, int offset, int limit, String sql, Object... params) {
    sql = buildPageSql(sql, offset, limit);
    return queryList(clazz, sql, params);
}

// 查詢單筆
public static <T> T queryOne(Class<T> clazz, String sql, Object... params) { ... }

// 統計總筆數(自動包裝 COUNT(*))
public static int count(String sql, Object... params) { ... }

參數綁定工具方法

private static void bindParams(PreparedStatement ps, Object... params) throws SQLException {
    if (params == null) return;
    for (int i = 0; i < params.length; i++) {
        Object v = params[i];
        int idx = i + 1;
        if (v == null) ps.setNull(idx, Types.VARCHAR);
        else if (v instanceof String) ps.setString(idx, (String) v);
        else if (v instanceof Integer) ps.setInt(idx, (Integer) v);
        else if (v instanceof Long) ps.setLong(idx, (Long) v);
        else if (v instanceof Double) ps.setDouble(idx, (Double) v);
        else if (v instanceof Boolean) ps.setBoolean(idx, (Boolean) v);
        else if (v instanceof Date) ps.setDate(idx, (Date) v);
        else if (v instanceof Timestamp) ps.setTimestamp(idx, (Timestamp) v);
        else ps.setObject(idx, v);
    }
}

使用範例

// 插入
User u = new User(); u.setName("Alice"); u.setEmail("alice@example.com");
DBUtil.insert("INSERT INTO users (user_name, email) VALUES (?, ?)", u);

// 分頁查詢
List<User> page = DBUtil.queryPage(User.class, 0, 10,
    "SELECT user_id, user_name, email FROM users WHERE email LIKE ?", "%@example.com");

// 統計
int total = DBUtil.count("SELECT * FROM users WHERE email LIKE ?", "%@example.com");

優缺點與改進方向

優點缺點/待改進
零依賴、輕量、支援多資料庫方言SQL 仍需手寫,無類型安全查詢 DSL
反射映射支援 @Column 自定義欄位效能較 ORM 稍低(可快取 Field/Column 資訊)
自動計數與分頁改寫複雜 JOIN/子查詢的欄位提取可能失敗
參數化防注入無連線池整合(需自行注入 DataSource)

生產級替代建議

  • MyBatis:XML/註解映射、動態 SQL、分頁插件、成熟生態。
  • JdbcTemplate (Spring):Spring 體系內建、RowMapper、NamedParameterJdbcTemplate。
  • JOOQ:類型安全 SQL DSL、代碼生成、複雜查詢首選。
  • Hibernate/JPA:全功能 ORM、實體生命週期、快取、適合複雜領域模型。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved