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

Java Swing JTable 綁定數據庫數據的兩種實現方案詳解

     編輯:Java 相關知識

前言

在 Java Swing 桌面應用開發中,JTable 是展示表格數據的核心組件。本文詳細講解兩種將數據庫查詢結果綁定到 JTable 的經典方案:二維數組模式與TableModel 模式,並提供完整代碼、關鍵步驟截圖說明及常見問題處理。

JTable 兩大構造器回顧

構造器參數適用場景
JTable(Object[][] rowData, Object[] columnNames)二維數組數據 + 列名數組數據量較小、一次性加載、結構固定
JTable(TableModel dm)實現 TableModel 接口的數據模型大數據量、動態更新、需要自定義渲染/編輯

方案一:二維數組綁定(基於 DefaultTableModel)

核心步驟

  1. 查詢數據庫獲取 ResultSet。
  2. 將 ResultSet 轉換為 Object[][] 二維數組。
  3. 定義列名 String[]。
  4. 創建 DefaultTableModel(data, columnNames) 並設置給 JTable。

關鍵代碼實現

public class UserDao {
    // 假設已有獲取 ResultSet 的方法(使用預編譯)
    public ResultSet getResultSet(String sql, Object[] params) throws SQLException {
        // ... 省略連接、預編譯、設置參數、執行查詢代碼
        return resultSet;
    }

    /** 將 ResultSet 轉為二維數組 */
    public Object[][] resultSetToArray(ResultSet rs) throws SQLException {
        // 需可滾動結果集以獲取行數
        rs.last();
        int rowCount = rs.getRow();
        rs.beforeFirst();

        ResultSetMetaData meta = rs.getMetaData();
        int colCount = meta.getColumnCount();

        Object[][] data = new Object[rowCount][colCount];
        int rowIdx = 0;
        while (rs.next()) {
            Object[] row = new Object[colCount];
            for (int i = 0; i < colCount; i++) {
                row[i] = rs.getObject(i + 1); // 保留原始類型,由渲染器格式化
            }
            data[rowIdx++] = row;
        }
        return data;
    }
}

Swing 界面代碼調用

public class UserTableFrame extends JFrame {
    private JTable userTable;

    public UserTableFrame() {
        initComponents();
        loadData();
    }

    private void loadData() {
        try {
            UserDao dao = new UserDao();
            ResultSet rs = dao.getResultSet("SELECT id, username, email FROM t_user", null);
            Object[][] data = dao.resultSetToArray(rs);
            String[] columns = {"ID", "用戶名", "郵箱"};

            userTable.setModel(new DefaultTableModel(data, columns));
        } catch (SQLException e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "數據加載失敗: " + e.getMessage());
        }
    }
}

優缺點分析

  • ✅ 簡單直觀,幾行代碼完成綁定。
  • ✅ 適合數據量較小(< 5000 行)的靜態展示。
  • ❌ 所有數據一次性載入內存,大數據量易 OOM。
  • ❌ 動態增刪改需手動操作模型數組,代碼分散。
  • ❌ 無法自定義單元格渲染、編輯器、排序等高級特性。

方案二:自定義 TableModel 綁定(推薦用於生產環境)

核心思路

實現 AbstractTableModel(或繼承 DefaultTableModel),封裝數據訪問邏輯,JTable 僅作為視圖組件,按需請求數據。

完整 TableModel 實現

public class UserTableModel extends AbstractTableModel {
    private final List userList = new ArrayList<>();
    private final String[] columnNames = {"ID", "用戶名", "郵箱", "狀態"};
    private final UserDao userDao = new UserDao();

    // --- AbstractTableModel 必須實現的三個方法 ---
    @Override
    public int getRowCount() {
        return userList.size();
    }

    @Override
    public int getColumnCount() {
        return columnNames.length;
    }

    @Override
    public String getColumnName(int column) {
        return columnNames[column];
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        User u = userList.get(rowIndex);
        switch (columnIndex) {
            case 0: return u.getId();
            case 1: return u.getUsername();
            case 2: return u.getEmail();
            case 3: return u.getStatus() == 1 ? "啟用" : "禁用";
            default: return null;
        }
    }

    // --- 可選:支持單元格編輯 ---
    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex) {
        return columnIndex == 3; // 僅允許編輯狀態列
    }

    @Override
    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        if (columnIndex == 3) {
            User u = userList.get(rowIndex);
            u.setStatus("啟用".equals(aValue) ? 1 : 0);
            // 同步更新數據庫
            userDao.updateStatus(u.getId(), u.getStatus());
            fireTableCellUpdated(rowIndex, columnIndex);
        }
    }

    // --- 業務方法 ---
    public void loadData() {
        try (ResultSet rs = userDao.getResultSet("SELECT id, username, email, status FROM t_user", null)) {
            userList.clear();
            while (rs.next()) {
                User u = new User();
                u.setId(rs.getLong("id"));
                u.setUsername(rs.getString("username"));
                u.setEmail(rs.getString("email"));
                u.setStatus(rs.getInt("status"));
                userList.add(u);
            }
            fireTableDataChanged(); // 通知視圖刷新
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void addUser(User u) {
        userDao.insert(u);
        userList.add(u);
        fireTableRowsInserted(userList.size() - 1, userList.size() - 1);
    }

    public void removeUser(int row) {
        User u = userList.remove(row);
        userDao.delete(u.getId());
        fireTableRowsDeleted(row, row);
    }
}

在可視化設計器中綁定(MyEclipse/IntelliJ GUI Designer)

  1. 拖拽 JTable 到面板。
  2. 右鍵 JTable → Properties → model 屬性。
  3. 選擇 Custom Code(或 Model from Code)。
  4. 填入靜態工廠方法:com.example.model.UserTableModel.createInstance()。
  5. 確保工廠方法返回 TableModel 實例。

靜態工廠方法示例

public class UserTableModel extends AbstractTableModel {
    // ... 前面的代碼 ...

    public static UserTableModel createInstance() {
        UserTableModel model = new UserTableModel();
        model.loadData(); // 初始化加載數據
        return model;
    }
}

優缺點分析

  • ✅ 數據與視圖分離,符合 MVC 模式。
  • ✅ 支持大數據分頁加載(配合 getRowCount 返回總數,getValueAt 按需查詢)。
  • ✅ 原生支持排序(TableRowSorter)、過濾、單元格渲染/編輯。
  • ✅ 業務邏輯集中在 Model,Controller/Frame 代碼極簡。
  • ❌ 初始代碼量較大,需理解 TableModel 事件機制(fireTableXxx 系列)。

進階技巧與常見問題

1. 日期/數字格式化顯示

覆寫 getColumnClass(int columnIndex) 返回具體類型,JTable 會自動使用對應渲染器:

@Override
public Class getColumnClass(int columnIndex) {
    if (columnIndex == 0) return Long.class;
    if (columnIndex == 3) return Boolean.class; // 狀態用勾選框渲染
    return String.class;
}

2. 大數據量分頁/虛擬化

不將所有數據載入 List,而是在 getValueAt 時按需查詢(需緩存當前頁數據),或使用 SwingX JXTable 等增強組件。

3. 線程安全

數據庫操作耗時,必須在後台線程(SwingWorker)執行,完成後在 EDT 更新模型:

new SwingWorker() {
    @Override
    protected Void doInBackground() {
        model.loadData(); // 後台查詢
        return null;
    }
    @Override
    protected void done() {
        model.fireTableDataChanged(); // EDT 刷新視圖
    }
}.execute();

總結與選型建議

場景推薦方案理由
原型演示、數據<1000行、無編輯需求方案一:二維數組代碼最少,快速見效
生產環境、需編輯/排序/過濾、數據>1000行方案二:自定義 TableModel架構清晰、擴展性強、效能可控
超大數據量(>10萬行)、分頁/虛擬滾動方案二 + 分頁緩存 或 JXTable避免全量載入內存

掌握 TableModel 機制是 Swing 表格開發的進階關鍵。建議初學者先用方案一跑通流程,再重構為方案二,體會 MVC 分離帶來的代碼組織優勢。

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