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

Apache DbUtils 入門到實戰:簡化 JDBC 操作的輕量級 ORM 工具庫完全指南

     編輯:Java 綜合教學

為什麼需要 DbUtils?

原生 JDBC 代碼冗長、重複:獲取連接、創建 Statement、設置參數、執行、遍歷 ResultSet 映射對象、關閉資源(finally 塊)。DbUtils(Apache Commons 組件)僅 1 個 JAR(~100KB),封裝了這些樣板代碼,讓 DAO 層開發回歸「業務邏輯」。

核心類速覽

類/接口核心作用
QueryRunner核心執行類,提供 update/query/batch 等方法
ResultSetHandler<T>策略接口,定義如何將 ResultSet 轉換為 Java 對象
DbUtils工具類:closeQuietly 靜默關閉連接/Statement/ResultSet
各種 Handler 實現BeanHandler、BeanListHandler、MapHandler、ScalarHandler 等

Maven 依賴

<dependency>
    <groupId>commons-dbutils</groupId>
    <artifactId>commons-dbutils</artifactId>
    <version>1.7</version> <!-- 或 1.8.1 -->
</dependency>
<!-- 配合 C3P0 連接池 -->
<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.5.5</version>
</dependency>

QueryRunner 兩種構造模式

// 1. 無參構造:每次操作手動傳 Connection,適合手動事務控制
QueryRunner qr = new QueryRunner();
qr.update(conn, "INSERT INTO user(name) VALUES(?)", "Tom");

// 2. 帶 DataSource 構造:自動獲取/關閉連接,自動提交事務(默認)
ComboPooledDataSource ds = new ComboPooledDataSource(); // 配置 c3p0-config.xml
QueryRunner qr = new QueryRunner(ds);
qr.update("INSERT INTO user(name) VALUES(?)", "Jerry"); // 無需手動連接

增刪改:update 方法

// 插入
int rows = qr.update("INSERT INTO user(name, age) VALUES(?, ?)", "Alice", 25);

// 更新
rows = qr.update("UPDATE user SET age=? WHERE name=?", 26, "Alice");

// 刪除
rows = qr.update("DELETE FROM user WHERE name=?", "Alice");

// 批量插入
Object[][] params = {{"Bob", 30}, {"Cathy", 28}, {"David", 35}};
int[] counts = qr.batch("INSERT INTO user(name, age) VALUES(?, ?)", params);

查詢:query 方法 + ResultSetHandler

常用 Handler 對照表

Handler返回類型適用場景
BeanHandler<T>(T.class)T單行 → 實體對象
BeanListHandler<T>(T.class)List<T>多行 → 實體列表(最常用)
MapHandler()Map<String, Object>單行 → 鍵值對(列名→值)
MapListHandler()List<Map<String, Object>>多行 → Map 列表
ArrayHandler()Object[]單行 → 數組
ArrayListHandler()List<Object[]>多行 → 數組列表
ScalarHandler<T>()T單值聚合:count/sum/max/min
ColumnListHandler("col")List<Object>單列值列表
KeyedHandler("keyCol")Map<Object, Map<String, Object>>按指定列分組

實戰示例

// 查詢所有用戶
List users = qr.query("SELECT * FROM user", new BeanListHandler<>(User.class));

// 查詢單個用戶
User user = qr.query("SELECT * FROM user WHERE id=?", new BeanHandler<>(User.class), 1);

// 統計總數
Long count = qr.query("SELECT COUNT(*) FROM user", new ScalarHandler<>());

// 自定義 Handler(複雜映射)
List users = qr.query("SELECT * FROM user", new ResultSetHandler>() {
    @Override
    public List handle(ResultSet rs) throws SQLException {
        List list = new ArrayList<>();
        while (rs.next()) {
            User u = new User();
            u.setId(rs.getInt("id"));
            u.setName(rs.getString("name"));
            // 手動處理關聯、嵌套對象等
            list.add(u);
        }
        return list;
    }
});

完整實戰案例:用戶聯系人管理系統(DAO 層)

實體類

public class User {
    private int id;
    private String name, password, email, sex, hobby, address, description;
    private Date birthday;
    private int age;
    // Getter/Setter/toString 省略
}

DAO 接口與實現

public interface UserDao {
    User findByName(String name);
    int register(User u);
    User login(String username, String password);
}

public class UserDaoImpl implements UserDao {
    private QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource()); // C3P0 DataSource

    @Override
    public User findByName(String name) {
        try {
            return qr.query("SELECT * FROM user WHERE name=?", new BeanHandler<>(User.class), name);
        } catch (SQLException e) {
            throw new RuntimeException("查詢用戶失敗", e);
        }
    }

    @Override
    public int register(User u) {
        String sql = "INSERT INTO user(name,password,age,sex,email,hobby,address,description,birthday) VALUES(?,?,?,?,?,?,?,?,?)";
        try {
            return qr.update(sql, u.getName(), u.getPassword(), u.getAge(), u.getSex(),
                    u.getEmail(), u.getHobby(), u.getAddress(), u.getDescription(), u.getBirthday());
        } catch (SQLException e) {
            return -2; // 註冊失敗
        }
    }

    @Override
    public User login(String username, String password) {
        try {
            return qr.query("SELECT * FROM user WHERE name=? AND password=?", new BeanHandler<>(User.class), username, password);
        } catch (SQLException e) {
            throw new RuntimeException("登錄查詢失敗", e);
        }
    }
}

工具類:JDBCUtils(C3P0 配置)

public class JDBCUtils {
    private static ComboPooledDataSource ds = new ComboPooledDataSource(); // 讀取 c3p0-config.xml

    public static DataSource getDataSource() { return ds; }

    // DbUtils 內部自動關閉,無需手動 close
}

事務處理(手動模式)

Connection conn = null;
try {
    conn = ds.getConnection();
    conn.setAutoCommit(false); // 開啟手動事務

    QueryRunner qr = new QueryRunner();
    qr.update(conn, "UPDATE account SET balance=balance-100 WHERE id=1");
    qr.update(conn, "UPDATE account SET balance=balance+100 WHERE id=2");

    conn.commit(); // 提交
} catch (SQLException e) {
    if (conn != null) conn.rollback(); // 回滾
    throw new RuntimeException("轉賬失敗", e);
} finally {
    DbUtils.closeQuietly(conn); // 靜默關閉
}

避坑指南與最佳實踐

  • 線程安全:QueryRunner 無狀態,可共享單例;DataSource 線程安全。
  • 參數順序:update/query(sql, param1, param2...) 順序必須與 SQL 佔位符 ? 一一對應。
  • BeanHandler 要求:實體類必須有**無參構造函數**,屬性名與列名一致(或用 BasicRowProcessor 自定義映射)。
  • 大數據量查詢:避免一次性 BeanListHandler 載入幾十萬行,改用 ResultSetHandler 流式處理或分頁查詢(LIMIT offset, size)。
  • 異常處理:捕獲 SQLException 後拋出 RuntimeException 或自定義異常,由全局異常處理器統一響應。
  • 版本選擇:1.7 穩定廣泛使用;1.8.1 支持 Java 8 Lambda、Optional、JSR-310 時間類型,推薦新項目用 1.8.1+。

總結

DbUtils 以極小的體積、零侵入的設計,將 JDBC 樣板代碼減少 80% 以上。它不是全功能 ORM(無緩存、無延遲加載、無關聯映射),但對於中小型項目、微服務、舊系統改造,是「輕量級、高性能、易上手」的最佳選擇。掌握 QueryRunner + Handler 組合模式,配合 C3P0/HikariCP 連接池,你的 DAO 層將乾淨利落、專注業務。

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