Derby 允許開發人員創建表功能。表功能是將外部數據打包使其具有 Derby 表的表現。外部數據可以是 XML、外部資料庫的表等等。本文將介紹 Derby 的表功能和使用方式,並討論如何優化表功能。
Derby 是什麼
Derby 是一款基於 Java 的,具有全面事務支持能力的,輕量級的關係型資料庫。 2004 年 IBM 在舊金山的 LinuxWorld 宣布正在將 Cloudscape 作為開放源代碼發布給 Apache Software Foundation (ASF),後者為其建立了一個名為“ Derby ”的孵化器項目。目前最新的版本是 10.4.2.0 。
Derby 的表功能(Derby-style table functions)
在一些資料庫產品中,也有一些表功能的不同實現。比如 Oracle 的表功能,它可以使用 PL/SQL 來產生一個結果集。但由於 Derby 本身是基於 Java 實現,開發人員可以更方便的使用 Java 語言開發表功能,使得其具有一定的靈活性。
Derby 的表功能可以將外部數據包裝成 Derby 中的表。外部數據可以是 XML 文件,文本文件,其它資料庫中的表或一個 RSS 的訂閱文件。這些信息源都可以被表示為一個 JDBC 的結果集 (ResultSet) 。
在開發人員實現表功能過程中,大部分的 ResultSet 中的方法都可以不用實現,但是一個合法的 Derby-style 的表功能必須實現以下方法。
Derby 表功能的開發
創建表功能
CREATE FUNCTION externalStudents () RETURNS TABLE ( Id INT, Name VARCHAR( 50 ), ) LANGUAGE JAVA PARAMETER STYLE DERBY_JDBC_RESULT_SET READS SQL DATA EXTERNAL NAME 'com.test.StudentTable.read' public static ResultSet read() {...} |
使用表功能
為了調用一個表功能,我們需要將一個 Table 的構造器放入一個查詢的 From 字句。需要注意的是,這個表的別名 s 是語法中必須的部分。
INTO students SELECT s.* FROM TABLE (externalStudents() ) s; |
一個使用 RSS 的表功能例子
在實際應用中,我們可能創建一個基於網路內容動態更新的資料庫。網路數據源包括 HTML 頁面數據,RSS 數據源,Web Service 服務等等。其中最直觀和流行的是 RSS 數據源,很多新聞網站和資訊網站都會提供相應的 RSS 源供用戶訂閱,RSS 以其 XML 格式標準成為一種統一的消息發布方式並得到廣泛應用。
下面我們以 IBM Developer Works 的 RSS 為我們的外部數據源,構建一個演示 RSS 表功能的例子。
先聲明我們要定義的表功能相關名稱:
屬性名 | 類型 | 描述 |
title | varchar(200) | 文章標題 |
description | varchar(500) | 文章摘要 |
link | varchar(200) | 鏈接 |
下面是構建 RSS 表功能函數的步驟,首先創建下面的表功能。
CREATE FUNCTION externalWorks () RETURNS TABLE ( title varchar(200), description VARCHAR(500) link varchar(200) ) LANGUAGE JAVA PARAMETER STYLE DERBY_JDBC_RESULT_SET READS SQL DATA EXTERNAL NAME 'com.test.WorksTable.read' |
其中 create function 定義了一個表功能,該表功能返回一個包含三個屬性的數據表。 EXTERNAL NAME 標識綁定的 Java 類方法,當執行表功能時會執行該方法。
這裡我們將表功能綁定到 "com.test.WorksTable" 類的 read 方法,該方法返回一個 ResultSet 實例。
下面是 ResultSet 實現和返回 ResultSet 實例的 Java 代碼。
package com.test public class WorksTable { public static ResultSet read() { return new WorksResultSet(); } } package com.test public class WorksResultSet implements ResultSet { private Element channel; private Iterator iterator; private SAXReader reader; private Element current; // 當前節點 // 最後一次用 getString 方法取到的屬性值,主要用於 wasNull 函數判斷值是否為空 private String theLastRead; public WorksResultSet() { String url = "http://www.ibm.com/developerworks/news/dw_dwtp.rss"; reader = new SAXReader(); Document doc = null; try { doc = reader.read(url); } catch (DocumentException e) { e.printStackTrace(); } channel = doc.getRootElement().element("channel"); iterator = channel.elementIterator("item"); } public void close() throws SQLException { channel = null; iterator = null; reader = null; current = null; } public String getString(int index) throws SQLException { if (index == 1) { return (theLastRead = current.elementText("title")); } else if (index == 2) { return (theLastRead = current.elementText("description")); } else if (index == 3) { return (theLastRead = current.elementText("link")); } else { throw new SQLException("index out of bounds"); } } public String getString(String name) throws SQLException { if (name.equals("title")) { return (theLastRead = current.elementText("title")); } else if (name.equals("description")) { return (theLastRead = current.elementText("description")); } else if (name.equals("link")) { return (theLastRead = current.elementText("link")); } else { throw new SQLException("no such column"); } } public boolean next() throws SQLException { if (iterator.hasNext()) { current = (Element) iterator.next(); return true; } else { return false; } } public boolean wasNull() throws SQLException { if (null == theLastRead) { return true; } else { return false; } } ..... // 省略了其它未實現的 ResultSet 方法 } |
在 WorksResultSet 的構造器中,我們採用 dom4j 開源庫里的 SAXReader 來讀取 RSS 數據源。 dom4j 是一個很流行的 XML 解析開源項目,性能在當前 XML 解析庫中是很好的,API 介面也定義的很簡單,方便使用。我們通過 SAXReader 取到 XML 文檔的引用,並將其保存在實例屬性中。
在 close 方法中,我們簡單將實例屬性置為空值。在 getString 方法中,我們根據傳進來的數值或字元串取相應的屬性值。 next 方法判斷文檔中是否還有節點元素,並將新節點賦值給 current 實例屬性。 wasNull() 是 ResultSet 判斷 SQL 值是否為空的方法,一般在 getString 取到屬性值后立即使用。 wasNull 根據 theLastRead 屬性來判斷最近所取 SQL 值是否為空。
ResultSet 介面中還有很多方法,但我們實現上述幾個方法就可以簡單完成一個表功能了,讀者可以根據需要添加其它方法的實現。在這裡,其它方法的實現只是簡單拋出 UnsupportedOperationException 異常。
最後我們在 Derby 資料庫中創建了 article 這樣的表。
create table article( title varchar(200), description VARCHAR(500), link varchar(200) ); |
然後將 RSS 中的資料庫插入到 article 的表中,externalWorks() 為之前創建的表功能函數。
INSERT INTO article SELECT s.* FROM TABLE (externalWorks()) s; |
最後我們可以對 article 表進行查詢,看哪些外部資料庫被導入。
select * from article |
下面是部分輸出 :
Create stand-alone Web services applications with Eclipse and Java SE 6 Easily build a fully functioning Web services server and client application using the Eclipse IDE, Java SE 6, and Apache Ant. Start by getting familiar with the Eclipse IDE and configuring the environment. Then run the application. http://www.ibm.com/developerworks/webservices /edu/ws-dw-ws-eclipse-javase1.html?ca=drs-tp4708 |
一個使用 XML 的表功能例子
類似於上面 RSS 的例子,只是修改了一下 ResultSet 實現類的代碼:在構造器里通過 SAXReader 直接讀取 XML 文件。
public WorksResultSet() { String xmlFileName = "dw_dwtp.xml"; SAXReader saxReader = new SAXReader(); Document doc = null; try { doc = saxReader.read(xmlFileName); } catch (DocumentException e) { e.printStackTrace(); } channel = doc.getRootElement().element("channel"); iterator = channel.elementIterator("item"); } |
表功能的優化
默認情況下,Derby 的優化器會認為一個表功能具有以下特徵。
開發人員可以提供給優化器更多的信息,從而覆蓋優化器的行為。下面描述如何使用。
[火星人 ] 開發 Derby 的表功能已經有615次圍觀