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

從零構建基於 JDBC 的資料存取層(DAL):完整實戰指南

     編輯:Java 相關知識

前言:為什麼需要資料存取層?

在傳統 JDBC 開發中,業務邏輯往往夾雜著「取得連線 → 建立 Statement → 執行 SQL → 處理 ResultSet → 關閉資源」等樣板程式碼,導致維護困難、SQL 散落各處、測試成本高。資料存取層(Data Access Layer, DAL)的核心價值在於:將資料庫操作細節封裝起來,對上層暴露語義清晰的 CRUD 介面(如 insertUser()、findById()),實現關注點分離。

一、領域模型:User 實體類設計

對應資料表 user(id, name, password, age),採用包裝類型(Integer)處理資料庫 NULL 值:

public class User {
    private Integer id;
    private String name;
    private String pass;
    private Integer age;

    // 無參建構子(反射/框架必備)
    public User() {}

    // 業務建構子(不含自增 ID)
    public User(String name, String pass, Integer age) {
        this.name = name; this.pass = pass; this.age = age;
    }

    // 完整建構子
    public User(Integer id, String name, String pass, Integer age) {
        this(name, pass, age); this.id = id;
    }

    // Getter/Setter 省略...
}

二、連線管理:ConnectionFactory 模式

集中管理 JDBC 參數,提供單點獲取連線,異常轉為非受檢異常簡化呼叫端:

public class ConnectionFactory {
    private static final String URL  = "jdbc:mysql://localhost:3306/testdb";
    private static final String USER = "testuser";
    private static final String PASS = "testpass";

    static { try { Class.forName("com.mysql.jdbc.Driver"); } catch (Exception e) { throw new RuntimeException(e); } }

    public static Connection getConnection() {
        try { return DriverManager.getConnection(URL, USER, PASS); }
        catch (SQLException e) { throw new RuntimeException("DB 連線失敗", e); }
    }
}
最佳實踐:生產環境請改用連線池(HikariCP、Druid),並從設定檔讀取參數。

三、DAO 介面定義

public interface UserDao {
    User getUser(int id);
    Set<User> getAllUsers();
    User getUserByNameAndPass(String name, String pass);
    boolean insertUser(User user);
    boolean updateUser(User user);
    boolean deleteUser(int id);
}

四、核心實作技巧

1. 統一 ResultSet 映射邏輯

private User mapRow(ResultSet rs) throws SQLException {
    User u = new User();
    u.setId(rs.getInt("id"));
    u.setName(rs.getString("name"));
    u.setPass(rs.getString("pass"));
    u.setAge(rs.getInt("age"));
    return u;
}

2. 單筆查詢:PreparedStatement 防注入

public User getUser(int id) {
    String sql = "SELECT * FROM user WHERE id = ?";
    try (Connection c = ConnectionFactory.getConnection();
         PreparedStatement ps = c.prepareStatement(sql)) {
        ps.setInt(1, id);
        try (ResultSet rs = ps.executeQuery()) {
            return rs.next() ? mapRow(rs) : null;
        }
    } catch (SQLException e) { throw new RuntimeException(e); }
}

3. 登入驗證:多參數綁定

public User getUserByNameAndPass(String name, String pass) {
    String sql = "SELECT * FROM user WHERE name = ? AND pass = ?";
    try (Connection c = ConnectionFactory.getConnection();
         PreparedStatement ps = c.prepareStatement(sql)) {
        ps.setString(1, name);
        ps.setString(2, pass);
        try (ResultSet rs = ps.executeQuery()) {
            return rs.next() ? mapRow(rs) : null;
        }
    } catch (SQLException e) { throw new RuntimeException(e); }
}

4. 批量查詢:使用 Set/List 收集結果

public Set<User> getAllUsers() {
    String sql = "SELECT * FROM user";
    Set<User> set = new HashSet<>();
    try (Connection c = ConnectionFactory.getConnection();
         Statement st = c.createStatement();
         ResultSet rs = st.executeQuery(sql)) {
        while (rs.next()) set.add(mapRow(rs));
    } catch (SQLException e) { throw new RuntimeException(e); }
    return set;
}

5. 寫操作:executeUpdate 回傳影響行數

public boolean insertUser(User u) {
    String sql = "INSERT INTO user(name, pass, age) VALUES (?, ?, ?)";
    try (Connection c = ConnectionFactory.getConnection();
         PreparedStatement ps = c.prepareStatement(sql)) {
        ps.setString(1, u.getName());
        ps.setString(2, u.getPass());
        ps.setInt(3, u.getAge());
        return ps.executeUpdate() == 1;
    } catch (SQLException e) { throw new RuntimeException(e); }
}

updateUser、deleteUser 同理,僅 SQL 不同。

五、資源管理:Try-with-resources(Java 7+)

上述所有範例均採用 try-with-resources 自動關閉 Connection、Statement、ResultSet,杜絕資源洩漏,程式碼更簡潔安全。

六、進階擴展方向

  • 事務支援:在 Service 層控制 Connection.setAutoCommit(false),配合 ThreadLocal 管理連線。
  • 分頁查詢:MySQL LIMIT ?,?、Oracle ROWNUM、SQL Server OFFSET FETCH。
  • ORM 框架遷移:此 DAL 結構可平滑遷移至 MyBatis/JPA,介面幾乎無需變動。

七、結語

一個乾淨的 DAL 不僅能消除重複程式碼,更能為後續引入連線池、快取、讀寫分離、分庫分表奠定堅實基礎。建議從「介面定義 → 實體映射 → 基礎 CRUD → 事務整合」循序漸進建設,讓資料庫操作真正成為業務邏輯的隱形後盾。

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