使用 Apache Jakarta POI 從 Excel 電子表格生成外部表

使用 Apache Jakarta POI 從 Excel 電子表格生成外部表
作者 Casimir Saternos

利用開放源代碼技術和外部表,將數據從 Excel 電子表格裝載到 Oracle 中。

本文相關下載
示例代碼
Oracle 數據庫 10g
Apache Jakarta POI

假定:您從經理那裏收到一封附帶 Excel 電子表格的電子郵件。您的任務是什麼?將電子表格中的數據載入到公司的 Oracle 9i/10g 數據庫中。您將會如何進行?你要編寫一個自定義的應用程序來裝載數據嗎?你要使用 SQL*Loader 實用程序嗎?同時還要考慮到 — 該電子表格包含幾個數據工作表。首先您必須以利用分隔符來保存每個工作表。事情比乍看上去時愈加複雜了……

DBA 和應用程序開發人員經常要將數據從 Excel 電子表格裝載到 Oracle 數據庫中。隨着 Oracle 10g 引入了一些類似電子表格的功能,更多當前在電子表格中存儲和查看的數據很可能會裝載到 Oracle 數據庫中進行操作和處理。

Oracle 提供了多種將數據從電子表格裝載到數據庫中的方法。大多數 Oracle DBA 和開發人員熟悉 SQL*Loader 的功能。Oracle HTML DB 也可用於以批處理方式裝載 Excel 數據。本文提供另一種從 Excel 電子表格裝載數據的方法,它利用了開放源代碼技術和外部表。

在本文中,您將瞭解如何使用 Apache Jakarta POI 開放源代碼項目來創建外部表,這些外部表可以引用帶多個數據工作表的 Excel 電子表格中的數據。在這一過程中,您將要創建一個名爲 ExternalTableGenerator 的自定義實用程序,以達到該目的。

如果給定一個包含一個或多個數據工作表的 Excel 電子表格,則 ExternalTableGenerator 實用程序可以生成純文本數據文件和一個 DDL 腳本,用戶可以運行它,可以在外部表中查看這些數據。此項目還有助於向那些剛接觸該特性的人闡明外部表的概念。

本文包含 ExternalTableGenerator 的源代碼(可以用作獨立的應用程序或者作爲更復雜和更強健的解決方案的基礎),還包含一個用於測試該過程的示例電子表格。

Jakarta POI

Jakarta POI 文件系統 API 使用純 Java 語言實現 OLE2 複合文檔格式,而 HSSF API 允許使用 Java POI 進行 Excel 文件的讀寫操作,這些操作包含示例代碼文件中,並在您運行 ExternalTableGenerator 時在 Java CLASSPATH 中對它們進行引用。

ExternalTableGenerator ExternalTableGenerator 使用了三個類,這些類利用 POI API 來處理電子表格。

 

  • ExternalTable.java — 該類表示外部表。ExternalTable 對象擁有名稱,引用文件系統中的目錄和文件,並擁有很多相應類型的列。該類還擁有屬性,這些屬性描述了錯誤文件名、日誌文件名、目錄位置以及用於各種文件 名擴展的常量。該類由這些屬性值填充,然後用於生成外部表使用的 DDL,該外部表對應於電子表格中給定工作表的結構。

    構造器使用一個參數以獲取表名稱(名稱中的所有空格都被替換爲下劃線)。ExternalTableGenerator 類使用特定工作表的名稱來設置該類中的 name 屬性。該 name 用於表名以及錯誤文件和日誌文件。當 ExternalTable 類的所有列和其他屬性都完成填充後(基於電子表格中的前兩行),可以調用 getDdl(),返回用於創建外部表的 DDL。 DDL 的結構在本文的“DDL 腳本”一節中予以說明。

     

  • ExternalTableColumn.java — 該類表示外部表的一列。其屬性包括列名及其類型。就當前的應用程序來看,該類型被限制爲 VARCHAR2 或 NUMBER,而且使用 POI 爲這些類型所提供的常量。VARCHAR2 長度由工作表中第二行的值所確定。在由 ExternalTableGenerator 類調用時,列名由工作表中的第一行所確定。該類將列中的任何空格替換爲下劃線,以確保 DDL 有效。

     

  • ExternalTableGenerator.java — 該類執行實際操作。它將讀取以參數形式傳遞給它的電子表格。對於電子表格中的每個工作表,它在當前目錄中生成一個以逗號對值進行分隔的文件(擴展名爲 .csv)。它還創建出用於創建目錄和外部表的 DDL。

處理過程必要的 POI 專用調用有以下:

execute() 方法中,以下的兩行代碼用於從文件系統中訪問電子表格,並創建一個新的工作單對象,該對象允許您操作電子表格。

 

POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(spreadsheet));

HSSFWorkbook wb = new HSSFWorkbook(fs);

訪問 HSSFWorkbook 對象時,您可以通過迭代所有的工作表、行和列來處理該對象。processWorkbook() 方法迭代工作單中的每個工作表,使用工作表名來創建 ExternalTable 對象,處理每個工作表,並提取相關數據來填充 ExternalTable 對象。

 

private void processWorkbook(HSSFWorkbook wb) {

for (int i = 0; i < wb.getNumberOfSheets(); i++)
{
HSSFSheet sheet = wb.getSheetAt(i);
ExternalTable table = new ExternalTable(wb.getSheetName(i));
processSheet(sheet, table);
System.out.println("...Table "+ table.getName()+ " processed." );
}
}

processSheet() 方法從工作表中獲取該表信息,寫入一個 .csv 文件(它將是 ExternalTable 引用的實際數據),並連續追加到含有 DDL 內容的字符串中。

getColumns() 方法包含對 POI API 的相關調用,用於檢索特定單元的數據。根據被訪問單元的類型,需要不同的方法調用。請注意,在處理我們示例中的數據時,必須考慮那些沒有數據的單元 (SSFCell.CELL_TYPE_BLANK)。

要將數據寫入某個工作表,writeCsv() 需要迭代相關的行和列,並創建一個字符串,其中包含逗號分隔的數據。它不寫出列名或者含有表示 VARCHAR2 大小的數據的行。write() 方法包含了將數據寫入文件系統的文件中的代碼。

您必須通過 SQL*Plus 來單獨運行 DDL 腳本(名爲 ExternalTables.sql),以便實際地創建目錄和外部表。

外部表

從 Oracle 9i 開始,外部表已經成爲 SQL*Loader 的一種替換方法。與傳統的數據庫表不同,外部表是引用那些存儲在數據庫之外的數據的只讀表。這些表能夠以標準 Oracle 表的相同方式進行查詢,並且經常作爲較大型 ETL(提取/轉換/裝載)過程的早期階段。外部表非常有助於引用數據庫外的數據,但是創建外部表需要相當多的 DDL 代碼,以創建引用數據文件的表、表的列定義和其他文件(錯誤文件和日誌文件)。

創建外部表需要了解數據文件的文件格式和記錄格式,還要了解 SQL。ExternalTableGenerator 自動執行該過程,並提供對於創建外部表(它們訪問帶分隔符的文件)的語法的一些見解。

DDL 腳本

以下是對 ExternalTableGenerator 所生成的 DDL 腳本的簡要描述,將把示例電子表格作爲一個參數來運行此腳本:

 

CREATE OR REPLACE DIRECTORY load_dir AS 'C:/workspaces/test/XL2ExternalTables'
;

CREATE OR REPLACE DIRECTORY 語句創建一個目錄對象,該對象允許 Oracle 訪問操作系統目錄。該目錄包含外部表所引用的數據文件以及錯誤文件和日誌文件。ExternalTableGenerator 使用當前工作目錄作爲所引用的操作系統目錄。

 

CREATE TABLE PA_Zip_Code_Locations
(
ZIP VARCHAR2(10),
AREANAME VARCHAR2(30),
LATITUDE NUMBER,
LONGITUDE NUMBER,
POPULATION NUMBER
)
ORGANIZATION EXTERNAL
(
TYPE oracle_loader
DEFAULT DIRECTORY load_dir
ACCESS PARAMETERS
(
RECORDS DELIMITED BY NEWLINE
badfile load_dir:'PA_Zip_Code_Locations.bad'
logfile load_dir:'PA_Zip_Code_Locations.log'
FIELDS TERMINATED BY ','
MISSING FIELD VALUES ARE NULL
(
ZIP,
AREANAME,
LATITUDE,
LONGITUDE,
POPULATION
))
LOCATION ('PA_Zip_Code_Locations.csv')
)REJECT LIMIT UNLIMITED;

CREATE TABLE 語法和列定義看上去類似創建常規 Oracle 表的典型 DDL 語句。ORGANIZATION EXTERNAL 將此表確定爲外部表。TYPE 子句用於指定驅動程序類型。隨後的子句描述了文件的結構以及日誌文件、錯誤文件和數據文件的位置。有關 EXTERNAL TABLE 語法的更多信息,請參見 Oracle 文檔

ExternalTableGenerator 根據以下規則生成 DDL 的以下片斷:

 

  • 表的名稱基於給定工作表的名稱。工作表的名稱還用於確定錯誤文件、日誌文件以及外部表所引用的數據文件的名稱。
  • 列的名稱基於工作表第一行中的值。這些名稱還用於外部表的 FIELDS 定義部分。
  • VARCHAR2 的長度由電子表格中的第二行所指定。
  • 列類型被定義爲 VARCHAR2 或 NUMBER,這取決於電子表格隨後一行中的單元。
  • 因爲每個 .xls 工作表都是導出以逗號對值進行分隔 (.csv) 文件,所以其中包含了 RECORDS DELIMITED BY NEWLINE 和 FIELDS TERMINATED BY ',' 信息。

new_departmentsnew_employees 表具有與以上所示的 pa_zip_code_locations 表相同的基本結構。

Excel 電子表格

需要裝載的數據經常具有由逗號、線段或製表符分隔數值的簡單格式,而裝載這些數據是相當簡單直接的 任務。但是,數據可以來自可能包含多個工作表的 Excel 電子表格。電子表格以二進制格式存儲,不能直接利用 SQL*Loader 或外部表進行訪問。Excel 電子表格優於傳統純文本文件的一個好處是可以對數據分類(分爲數值型、字符串型等等)。如上所述,您可以在 ExternalTableGenerator 中利用這一情況來確定列類型。

對於 ExternalTableGenerator 所處理的電子表格有一些要求:

 

  • 它們可以包含一個或多個電子表格。
  • 電子表格的名稱用於表名定義。外部表名、數據文件名、日誌文件名和錯誤文件名由電子表格的名稱指定。
  • 電子表格中的每個工作表用於生成外部表 DDL 的一個單元塊和一個 .csv 數據文件。
  • 每個電子表格中的第一行是列名定義行。列的名稱用於 EXTERNAL TABLE 單元塊的列名和字段名部分。該行不包括在 .csv 數據文件中。
  • 每個電子表格中的第二行用於確定 VARCHAR2 字段的長度。此行也不包括在 .csv 數據文件中。

由於您使用電子表格的各部分來確定表名和列名,請謹慎選擇那些能夠生成有效 SQL 的名稱。換言之,避免由 ExternalTableGenerator 處理電子表格中的非法字符、SQL 關鍵字、現有的表名以及類似內容。

運行該項目不需要 Excel,但是查看 .xls 文件需要它。示例數據的屏幕快照如下:

圖 1

圖 2

圖 3

示例中的數據表示需要添加到數據庫中的兩個新部門、其員工以及相關的郵政編碼列表。員工和部門數據 基於 scott/tiger 模式表,如果您向 scott 授予所有必需的權限,則可以導入這些數據以供實驗。郵政編碼信息表明 ExternalTableGenerator 能夠處理更多的數據。

運行該項目

該項目在 Java 運行時環境 (JRE) 1.4.2_03 中完成了測試,但是它應該可以在任何能夠運行 POI 的 JRE 中運行。您可以通過執行 runSample.bat 來運行 ExternalTableGenerator。這個批處理文件使用了在參數中所指定的 Excel 工作表,將相關的 jar 包含在類路徑中 (XL2ExternalTables.jar;jakarta-poi-1.5.1-final-20020615.jar;jakarta-poi-contrib-1.5.1-final-20020615.jar) 並運行適當的 Java 類 com.saternos.database.utilities.ExternalTableGenerator。

示例成功運行後所得到的輸出應該類似於以下內容(其路徑基於您的工作目錄)。

 

C:/Documents and Settings/Administrator/Desktop/XL2ETB>runExample
C:/Documents and Settings/Administrator/Desktop/XL2ETB>
java -cp XL2ExternalTables.jar;jakarta-poi-1.5.1-final-20020615.jar;
jakarta-poi-contrib-1.5.1-final-20020
615.jar com.saternos.database.utilities.ExternalTableGenerator new_department_data.xls
Begin processing.
Using working directory C:/Documents and Settings/Administrator/Desktop/XL2ETB
...File PA_Zip_Code_Locations.csv created.
...Table PA_Zip_Code_Locations processed.
...File New_Departments.csv created.
...Table New_Departments processed.
...File New_Employees.csv created.
...Table New_Employees processed.
...File ExternalTables.sql created.
Processing complete.

這些代碼創建了三個數據文件(指定使用 .csv 擴展名)並生成一個的 SQL 腳本,該腳本包含用於創建外部表的 DDL。

以下是運行該 DDL 腳本並測試所得結果的一個示例。通過 SQL*Plus 從創建 ExternalTables.sql 腳本的目錄連接到數據庫,開始進行工作。

 

C:/XL2ETB>SQL*Plus testuser/mypassword@orcladm
SQL*Plus:Release 10.1.0.2.0 - Production on Tue Dec 21 09:36:25 2004
Copyright (c) 1982, 2004, Oracle.All rights reserved.
Connected to:
Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.1.0 - Production
SQL> select * from tab;
no rows selected

雖然本示例是一個空的模式,但是如果用戶擁有適當的權限,並且沒有名稱衝突,則該腳本可以在任何模式中運行。

 

SQL> @ExternalTables
Directory created.
Table created.
Table created.
Table created.
該腳本被執行,並且創建了目錄對象和三個外部表。
SQL> select * from tab;
TNAME TABTYPE CLUSTERID
------------------------------ ------- ----------
NEW_DEPARTMENTS TABLE
NEW_EMPLOYEES TABLE
PA_ZIP_CODE_LOCATIONS TABLE
SQL> select count(*) from new_departments;
COUNT(*)
----------
2
SQL> select count(*) from new_employees;
COUNT(*)
----------
14
SQL> select count(*) from pa_zip_code_locations;

COUNT(*)
----------
1458
SQL> select * from new_employees;


EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
------ ---------- --------- ---------- ------------ -------- ---------- ----------
9499 ALDEN SALESMAN 9698 33289.0 3200 1300 50

9521 WORD SALESMAN 9698 33291.0 2500 1000 50

9654 MALTIN SALESMAN 9698 33509.0 2500 4400 50

EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
------ ---------- --------- ---------- ------------- -------- ---------- ----------
9844 TURKER SALESMAN 9698 33489.0 3000 0 50


SQL>

結論

本文介紹了 POI,但只是淺顯地討論了通過該 API 可以使用的功能。有多種方法可以增強本文中所描述的 ExternalTableGenerator 實用程序的功能:

 

  • 在該實用程序中可以建立一個 JDBC 連接,使外部表 DDL 能夠在該實用程序內部執行。
  • 可以添加基於電子表格類型的其他列類型和精度。
  • 該實用程序可以添加功能以便操作外部表的其他屬性(PARALLEL 等)。
  • 可以設計出將電子表格類型轉換爲 Oracle 數據類型的其他方法。

本文有可能使您更希望探索通過協同使用 POI 和 Oracle 來促進在 Excel 與 Oracle 之間進行數據操作的可能性。


Casimir Saternos 是 Oracle 認證 DBA、IBM 認證企業開發人員和 Sun 認證 Java 程序員,住在賓夕法尼亞州 Allentown。 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章