歡迎您光臨本站 註冊首頁

開發 Derby 的表功能

←手機掃碼閱讀     火星人 @ 2014-03-12 , reply:0
  
Derby 允許開發人員創建表功能。表功能是將外部數據打包使其具有 Derby 表的表現。外部數據可以是 XML、外部資料庫的表等等。本文將介紹 Derby 的表功能和使用方式,並討論如何優化表功能。

Derby 是什麼

Derby 是一款基於 Java 的,具有全面事務支持能力的,輕量級的關係型資料庫。 2004 年 IBM 在舊金山的 LinuxWorld 宣布正在將 Cloudscape 作為開放源代碼發布給 Apache Software Foundation (ASF),後者為其建立了一個名為“ Derby ”的孵化器項目。目前最新的版本是 10.4.2.0 。

  • Derby 是一個輕量級的資料庫。它大概 2M 左右,包括基本的資料庫引擎和內嵌的 JDBC 驅動。
  • 它基於 Java, JDBC 和 SQL 標準。
  • 它提供了內嵌的 JDBC 驅動,使得開發人員可以將 Derby 嵌入基於 Java 的應用解決方案。
  • 它同時擁有 Derby 客戶端 JDBC 驅動和 Derby 伺服器,以支持傳統意義上資料庫的客戶機 / 伺服器模式。
  • 它易於安裝和使用。




Derby 的表功能(Derby-style table functions)

在一些資料庫產品中,也有一些表功能的不同實現。比如 Oracle 的表功能,它可以使用 PL/SQL 來產生一個結果集。但由於 Derby 本身是基於 Java 實現,開發人員可以更方便的使用 Java 語言開發表功能,使得其具有一定的靈活性。

Derby 的表功能可以將外部數據包裝成 Derby 中的表。外部數據可以是 XML 文件,文本文件,其它資料庫中的表或一個 RSS 的訂閱文件。這些信息源都可以被表示為一個 JDBC 的結果集 (ResultSet) 。

在開發人員實現表功能過程中,大部分的 ResultSet 中的方法都可以不用實現,但是一個合法的 Derby-style 的表功能必須實現以下方法。

  • next()
  • close()
  • wasNull()
  • getXXX() -- 當調用一個 Derby 表功能時,Derby 針對相應的列調用 get***() 方法。所謂的 get***() 方法是基於相應列的數據類型。




Derby 表功能的開發

創建表功能


清單 1. 創建表功能
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 表功能的例子。

先聲明我們要定義的表功能相關名稱:

  • 表功能名稱:externalWorks
  • 表功能綁定的 Java 類名稱:WorksTable
  • ResultSet 實現類:WorksResultSet
  • 資料庫表名稱:article
  • 資料庫表結構:如表 1 所示

表 1. 資料庫表結構
屬性名 類型 描述
title varchar(200) 文章標題
description varchar(500) 文章摘要
link varchar(200) 鏈接

下面是構建 RSS 表功能函數的步驟,首先創建下面的表功能。


清單 2. 構建 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 代碼。


清單 3. 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 

下面是部分輸出 :


清單 4. 部分輸出
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 文件。


清單 5. 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 的優化器會認為一個表功能具有以下特徵。

  1. 低效的。表功能中的行的創建和循環是低效率的。這使得優化器會將表功能放入連接次序的外部從而降低循環的頻度。
  2. 可重複的。有些表功能經常會被實例化多次,並且具有同樣的結果。但有些表功能也許只會讀取一次。如果優化器知道一個表功能是可重複的,從而將表功能放入內部的 slot,以提高效率。如果一個表功能不會被重複讀取,優化器可以將其放入更外部的 slot,或者將其內容放入一個臨時表格。

開發人員可以提供給優化器更多的信息,從而覆蓋優化器的行為。下面描述如何使用。

  • 無參的構造器。 表功能的類必須有一個無參的 public 的構造器。
  • VTI 開銷評估方法。表功能類必須實現 org.apache.derby.vit.VTICosting 介面,這需要實現下列幾個方法:
    1. getEstimatedCostPerInstantiation() 方法,這個方法能夠評估創建表功能及遍歷表中所有行的開銷。這個方法的返回結果是由累加創建開銷和遍歷開銷兩個評估結果得到的,創建開銷是每次創建一個表時都存在的開銷,即使創建的是一個不含任何數據的空表。遍歷開銷是遍歷搜索表中所有行所需的開銷。
      通常情況下,和遍歷開銷相比,創建開銷是微不足道的,可以忽略不計,尤其是表中包含大量數據時。但當表中僅含少量數據,而系統又需要創建大量的表功能時,表功能的創建開銷就會佔據很大的比重,不能被忽略了。
    2. getEstimatedRowCount() 方法,這個方法用於估算通過表功能創建的表的總行數。
    3. supportsMultipleInstantiations() 方法,通過這個方法能夠評估出表功能是否會多次實例化。(責任編輯:A6)


[火星人 ] 開發 Derby 的表功能已經有615次圍觀

http://coctec.com/docs/linux/show-post-68961.html