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

在 Java 桌面應用中嵌入 Java DB (Apache Derby) 完整實戰指南

     編輯:Java 程式設計入門

前言

Java DB 是 Sun (現 Oracle) 釋出的、基於 Apache Derby 的 100% Java 輕量級關聯式資料庫,僅 2 MB、無需獨立進程、可直接嵌入桌面應用。本文以「通訊錄」範例,完整示範從下載、安裝、NetBeans 整合、JDBC 連線、建立資料表、CRUD 操作到應用程式部署的全流程。

環境準備

  1. 下載:從 Apache Derby 官網 取得最新穩定版 (如 10.17.1.0),解壓取 lib/derby.jar。
  2. NetBeans 整合:Tools → Libraries → New Library → "JavaDBEmbedded" → Add JAR/Folder → 選擇 derby.jar → Javadoc 標籤指向 docs/javadoc。
  3. 專案類別路徑:將 Library 加入專案,或用 Ant/Maven 管理依賴。

核心類別架構

  • AddressFrame:主視窗 (JFrame),協調子元件事件。
  • AddressPanel:顯示/編輯單筆記錄的表單。
  • AddressListPanel:左側列表,持有 ListEntry (封裝主鍵 ID)。
  • AddressActionPanel:工具列按鈕 (新增/刪除/編輯/儲存/取消)。
  • AddressDao:資料存取物件 (DAO),封裝所有 JDBC 操作,達成業務邏輯與持久層解耦。
  • Address:POJO,對應 APP.ADDRESS 資料表欄位。

關鍵 JDBC 操作詳解

1. 載入嵌入式驅動

Class.forName("org.apache.derby.jdbc.EmbeddedDriver");

驅動類別在 derby.jar 內,無需額外安裝。

2. 設定資料庫系統目錄與連線

// 以使用者家目錄下 .addressbook 為系統目錄
String sysDir = System.getProperty("user.home") + "/.addressbook";
System.setProperty("derby.system.home", sysDir);

// 連線 URL:建立/開啟 DefaultAddressBook,帶入帳密
String url = "jdbc:derby:DefaultAddressBook;create=true;user=dbuser;password=dbuserpwd";
Connection conn = DriverManager.getConnection(url);
  • derby.system.home 統一管理所有資料庫檔案位置。
  • create=true 讓首次連線自動建立資料庫目錄與檔案。
  • 帳密可寫在 URL 或 Properties 物件中。

3. 建立資料表 (僅首次執行需執行)


String ddl = "CREATE TABLE APP.ADDRESS (" +
    "ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1) PRIMARY KEY," +
    "LASTNAME VARCHAR(30), FIRSTNAME VARCHAR(30), MIDDLENAME VARCHAR(30)," +
    "PHONE VARCHAR(20), EMAIL VARCHAR(30)," +
    "ADDRESS1 VARCHAR(30), ADDRESS2 VARCHAR(30)," +
    "CITY VARCHAR(30), STATE VARCHAR(30)," +
    "POSTALCODE VARCHAR(20), COUNTRY VARCHAR(30))";

try (Statement stmt = conn.createStatement()) {
    stmt.execute(ddl);
}
  • 主鍵 ID 使用 Derby 內建 IDENTITY 自動遞增。
  • 所有文字欄位採 VARCHAR,對應 Java String (UTF-16)。

4. DAO 核心方法:PreparedStatement 重用


public class AddressDao {
    private Connection conn;
    private PreparedStatement stmtInsert, stmtUpdate, stmtDelete, stmtSelectAll, stmtSelectById;

    public AddressDao() {
        // 初始化連線與 PreparedStatements (略)
        stmtInsert = conn.prepareStatement(
            "INSERT INTO APP.ADDRESS (LASTNAME, FIRSTNAME, MIDDLENAME, PHONE, EMAIL, " +
            "ADDRESS1, ADDRESS2, CITY, STATE, POSTALCODE, COUNTRY) VALUES (?,?,?,?,?,?,?,?,?,?,?)",
            Statement.RETURN_GENERATED_KEYS); // 取回自動生成的 ID
        stmtUpdate = conn.prepareStatement(
            "UPDATE APP.ADDRESS SET LASTNAME=?, FIRSTNAME=?, MIDDLENAME=?, PHONE=?, EMAIL=?, " +
            "ADDRESS1=?, ADDRESS2=?, CITY=?, STATE=?, POSTALCODE=?, COUNTRY=? WHERE ID=?");
        stmtDelete = conn.prepareStatement("DELETE FROM APP.ADDRESS WHERE ID=?");
        stmtSelectById = conn.prepareStatement("SELECT * FROM APP.ADDRESS WHERE ID=?");
        stmtSelectAll = conn.prepareStatement("SELECT * FROM APP.ADDRESS ORDER BY LASTNAME, FIRSTNAME");
    }

    public int saveRecord(Address addr) throws SQLException {
        bindAddress(stmtInsert, addr);
        stmtInsert.executeUpdate();
        try (ResultSet keys = stmtInsert.getGeneratedKeys()) {
            if (keys.next()) return keys.getInt(1);
        }
        return -1;
    }

    public boolean editRecord(Address addr) throws SQLException {
        bindAddress(stmtUpdate, addr);
        stmtUpdate.setInt(12, addr.getId());
        return stmtUpdate.executeUpdate() == 1;
    }

    public boolean deleteRecord(int id) throws SQLException {
        stmtDelete.setInt(1, id);
        return stmtDelete.executeUpdate() == 1;
    }

    public Address findById(int id) throws SQLException {
        stmtSelectById.setInt(1, id);
        try (ResultSet rs = stmtSelectById.executeQuery()) {
            return rs.next() ? mapRow(rs) : null;
        }
    }

    public List
findAll() throws SQLException { List
list = new ArrayList<>(); try (ResultSet rs = stmtSelectAll.executeQuery()) { while (rs.next()) list.add(mapRow(rs)); } return list; } private void bindAddress(PreparedStatement ps, Address a) throws SQLException { ps.setString(1, a.getLastName()); ps.setString(2, a.getFirstName()); ps.setString(3, a.getMiddleName()); ps.setString(4, a.getPhone()); ps.setString(5, a.getEmail()); ps.setString(6, a.getAddress1()); ps.setString(7, a.getAddress2()); ps.setString(8, a.getCity()); ps.setString(9, a.getState()); ps.setString(10, a.getPostalCode()); ps.setString(11, a.getCountry()); } private Address mapRow(ResultSet rs) throws SQLException { Address a = new Address(); a.setId(rs.getInt("ID")); a.setLastName(rs.getString("LASTNAME")); // ... 其餘欄位映射 return a; } }

5. UI 事件與 DAO 串聯

  • 新增:清空表單 → 使用者填寫 → 按「儲存」 → dao.saveRecord() → 取回 ID → 更新 AddressListPanel。
  • 編輯:列表選取 → dao.findById(id) 填充表單 → 修改 → 「儲存」 → dao.editRecord()。
  • 刪除:列表選取 → 確認對話框 → dao.deleteRecord(id) → 移除列表項目。

應用程式部署

嵌入式模式下,發布僅需兩個 JAR:

  1. 應用程式 JAR (含編譯後的類別與資源)。
  2. derby.jar。

使用 Ant/Maven 打包為單一可執行 JAR (含 Class-Path: derby.jar Manifest),或用 jlink (JDK 9+) 產生自含執行映像。資料庫檔案位於使用者 ~/.addressbook/DefaultAddressBook,隨使用者資料漫遊,無需管理員權限。

常見問題與對策

結語

Java DB (Derby) 以零配置、零安裝、純 Java 的特性,成為桌面應用本地持久化的理想選擇。配合 DAO 模式隔離資料庫細節,未來若需遷移至 SQLite、H2 或客戶端/伺服器模式,僅需替換 AddressDao 實作,業務邏輯與 UI 完全不變。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
問題原因解決
啟動報 java.sql.SQLException: Database 'DefaultAddressBook' not found忘記 create=true 或系統目錄錯誤檢查 derby.system.home 設定與 URL 參數
並發存取拋 LOCK_TIMEOUT嵌入式模式同一 JVM 內單執行緒安全,多執行緒需同步DAO 方法加 synchronized 或改用連接池 (Derby 支援網路伺服器模式)
中文亂碼資料庫建立時未指定 territory/collationURL 加 ;territory=zh_TW;collation=TERRITORY_BASED