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

Java 產出 Excel 報表實戰:基於 JXL API 完整範例與最佳實踐

     編輯:Java 相關知識

前言

JXL (Java Excel API) 是輕量級純 Java 讀寫 Excel (.xls) 庫,雖已停止維護(不支援 .xlsx),但在舊系統維護、簡單報表匯出仍廣泛使用。本文提供完整可運行範例,涵蓋資料庫查詢、樣式格式化、日期/數字格式、異常處理與資源釋放。

一、Maven 依賴

<dependency>
  <groupId>net.sourceforge.jexcelapi</groupId>
  <artifactId>jxl</artifactId>
  <version>2.6.12</version>
</dependency>

二、完整程式碼範例

import jxl.*;
import jxl.write.*;
import jxl.format.*;
import java.io.File;
import java.sql.*;
import java.util.Date;

public class ExcelReportGenerator {

    public void generateReport(String jdbcUrl, String user, String pass,
                               String sql, String outputPath) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        WritableWorkbook workbook = null;

        try {
            // 1. 資料庫連線
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection(jdbcUrl, user, pass);
            pstmt = conn.prepareStatement(sql);
            rs = pstmt.executeQuery();

            // 2. 建立 Excel 工作簿
            File file = new File(outputPath);
            workbook = Workbook.createWorkbook(file);
            WritableSheet sheet = workbook.createSheet("報表數據", 0);

            // 3. 定義樣式
            WritableFont headerFont = new WritableFont(WritableFont.ARIAL, 12, WritableFont.BOLD, false,
                    UnderlineStyle.NO_UNDERLINE, Colour.BLUE);
            WritableCellFormat headerFormat = new WritableCellFormat(headerFont);
            headerFormat.setAlignment(Alignment.CENTRE);
            headerFormat.setBackground(Colour.GRAY_25);

            WritableFont titleFont = new WritableFont(WritableFont.ARIAL, 10, WritableFont.BOLD, false,
                    UnderlineStyle.NO_UNDERLINE, Colour.RED);
            WritableCellFormat titleFormat = new WritableCellFormat(titleFont);

            WritableFont dataFont = new WritableFont(WritableFont.ARIAL, 10, WritableFont.NO_BOLD, false,
                    UnderlineStyle.NO_UNDERLINE, Colour.BLACK);
            WritableCellFormat dataFormat = new WritableCellFormat(dataFont);
            dataFormat.setBorder(Border.ALL, BorderLineStyle.THIN);

            // 數字格式:保留 5 位小數
            NumberFormat numFmt = new NumberFormat("0.00000");
            WritableCellFormat numberFormat = new WritableCellFormat(dataFont, numFmt);
            numberFormat.setBorder(Border.ALL, BorderLineStyle.THIN);

            // 日期格式
            DateFormat dateFmt = new DateFormat("yyyy-MM-dd");
            WritableCellFormat dateFormat = new WritableCellFormat(dataFont, dateFmt);
            dateFormat.setBorder(Border.ALL, BorderLineStyle.THIN);

            // 4. 寫入標題列
            ResultSetMetaData meta = rs.getMetaData();
            int colCount = meta.getColumnCount();
            for (int i = 1; i <= colCount; i++) {
                Label label = new Label(i - 1, 0, meta.getColumnName(i), headerFormat);
                sheet.addCell(label);
            }

            // 5. 寫入數據列
            int row = 1;
            while (rs.next()) {
                for (int i = 1; i <= colCount; i++) {
                    int colType = meta.getColumnType(i);
                    Object value = rs.getObject(i);
                    WritableCell cell;

                    if (value == null) {
                        cell = new Label(i - 1, row, "", dataFormat);
                    } else if (colType == Types.INTEGER || colType == Types.BIGINT ||
                               colType == Types.SMALLINT || colType == Types.TINYINT) {
                        cell = new Number(i - 1, row, rs.getLong(i), dataFormat);
                    } else if (colType == Types.DECIMAL || colType == Types.NUMERIC ||
                               colType == Types.REAL || colType == Types.FLOAT ||
                               colType == Types.DOUBLE) {
                        cell = new Number(i - 1, row, rs.getDouble(i), numberFormat);
                    } else if (colType == Types.DATE || colType == Types.TIMESTAMP) {
                        cell = new DateTime(i - 1, row, rs.getTimestamp(i), dateFormat);
                    } else {
                        cell = new Label(i - 1, row, value.toString(), dataFormat);
                    }
                    sheet.addCell(cell);
                }
                row++;
            }

            // 6. 自動調整欄寬 (可選)
            for (int i = 0; i < colCount; i++) {
                sheet.setColumnView(i, 20);
            }

            workbook.write();
            System.out.println("Excel 報表產生成功:" + outputPath);

        } catch (Exception e) {
            throw new RuntimeException("產生 Excel 報表失敗: " + e.getMessage(), e);
        } finally {
            // 7. 關鍵:正確關閉資源順序
            try { if (workbook != null) workbook.close(); } catch (Exception ignore) {}
            try { if (rs != null) rs.close(); } catch (Exception ignore) {}
            try { if (pstmt != null) pstmt.close(); } catch (Exception ignore) {}
            try { if (conn != null) conn.close(); } catch (Exception ignore) {}
        }
    }

    public static void main(String[] args) {
        new ExcelReportGenerator().generateReport(
            "jdbc:mysql://localhost:3306/testdb?useUnicode=true&characterEncoding=UTF-8",
            "root", "password",
            "SELECT id, name, price, create_time FROM products",
            "D:/reports/products.xls"
        );
    }
}

三、關鍵技術點解析

3.1 樣式複用原則

  • WritableCellFormat 為不可變物件,**同一格式全表共用單一實例**,避免建立大量重複格式物件導致記憶體暴增。
  • 字體、顏色、邊框、對齊、背景皆可組合。

3.2 數字/日期格式化

  • NumberFormat / DateFormat 傳入 Java 標準格式字串(0.00000、yyyy-MM-dd)。
  • 需搭配基底字體建立 WritableCellFormat(font, format)。

3.3 資料類型對應

透過 ResultSetMetaData.getColumnType() 判斷 JDBC 類型,映射至 JXL Label/Number/DateTime,確保 Excel 儲存格類型正確(而非全存為文字)。

3.4 大數據量優化

  • JXL 將整個 Workbook 保持記憶體,**不適合超過 10 萬列**報表(易 OOM)。
  • 大數據建議改用 Apache POI SXSSF (Streaming) 或分批寫入多個檔案。

四、常見坑點與解決方案

問題原因解決
中文亂碼JVM 預設編碼非 UTF-8JDBC URL 加 characterEncoding=UTF-8;JXL 內部用 UTF-16 無需額外設定
日期顯示為數字未套用 DateFormat建立 DateTime 時傳入 WritableCellFormat(dateFont, dateFormat)
數字科學記號預設格式套用 NumberFormat("0.00") 等固定小數格式
欄寬過窄預設寬度sheet.setColumnView(col, width) 或計算內容長度動態設定
資源洩漏未正確 close Workbook/Connectionfinally 區塊分別 try-catch 關閉,順序:Workbook → ResultSet → Statement → Connection

五、遷移建議:JXL → Apache POI

  • 新專案**強烈建議直接使用 Apache POI** (支援 .xlsx、SXSSF 流式寫入、活躍維護)。
  • API 對應:Workbook→Workbook、Sheet→Sheet、Label→Cell.setCellValue(String)、Number→Cell.setCellValue(double)。
  • POI 樣式同樣採用共用實例模式,核心概念相通。

六、總結

JXL 以簡潔 API 實現 Excel .xls 產出,核心在於樣式物件複用、類型正確映射、資源安全釋放三大原則。雖屬經典庫,上述最佳實踐在遷移至 POI 或其他報表工具時同樣適用,建議開發者內化為報表開發肌肉記憶。

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