程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java理論與實踐:在沒有數據庫的情況下進行數據庫查詢

Java理論與實踐:在沒有數據庫的情況下進行數據庫查詢

編輯:關於JAVA

我最近仔細考察了一個項目,該項目涉及相當多的 Web 快速搜索。當爬蟲程 序爬過不同的 Web 站點時,它將建立一個數據庫,該數據庫中包括它所爬過的 站點和網頁、每一頁所包含的鏈接、每一頁的分析結果等數據。最終結果是一組 報告,詳細說明經過了哪些站點和頁面、哪些是一直鏈接的、哪些鏈接已經斷開 、哪些頁面有錯誤、計算出的頁面規格,等等。開始的時候,沒人確切知道需要 什麼樣的報告,或者應當采用什麼樣的格式 —— 只知道有一些內容要報告。這 表明報告開發階段會是一個反復的階段,要經過多次反饋、修改,並且可能嘗試 使用不同的結構。惟一確定的報告要求是,報告應當以 XML 形式展示,也可能 以 HTML 形式展示。因此,開發和修改報告的過程必須是輕量級的,因為報告要 求是“動態發現”的,而不是預先指定的。

不需要數據庫

對這個問題的“最顯而易見的”解決方法是將所有東西都放入 SQL 數據庫中 —— 頁面、鏈接、度量標准、HTTP 結果代碼、計時結果和其他元數據。這個問 題可以借助關系表示來很好地解決,特別是因為這種方法不需要存儲已訪問頁面 的內容,只需要存儲它們的結構和元數據。

到目前為止,這個項目看起來像是一個典型的數據庫應用程序,並且它並不 缺少可供選擇的持久性策略。但是,或許可以避免使用數據庫持久存儲數據的復 雜性 —— 這個快速搜索工具(crawler)只訪問數萬個頁面。這個數字不是很 大,因此可以將整個數據庫放在內存中,當需要持久存儲數據時,可以通過序列 化來實現它。(是的,加載和保存操作要花費較長的時間,但是這些操作並不經 常執行。)懶惰反而帶來了一個好處 —— 不需要處理持久性極大地縮短了開發 應用程序的時間,因而顯著地減少了開發工作量。構建和操縱內存中的數據結構 要比每次添加、提取或者分析數據時都使用數據庫容易得多。不管選擇了哪種持 久存儲模型,都會限制任何觸及到數據的代碼的構造。

內存中的數據結構是一種樹型結構,如清單 1 所示,它的根是快速搜索過的 各個網站的主頁,因此 Visitor 模式是搜索這些主頁或者從中提取數據的理想 模式。(構建一個防止陷入鏈接循環 —— A 鏈接到 B、B 鏈接到 C、C 鏈接到 A —— 的基本 Visitor 類並不是很難。)

清單 1. Web 爬行器的一個簡化方案

public class Site {
   Page homepage;
   Collection<Page> pages;
   Collection<Link> links;
}
public class Page {
   String url;
   Site site;
   PageMetrics metrics;
}
public class Link {
   Page linkFrom;
   Page linkTo;
   String anchorText;
}

這個快速搜索工具的應用程序中有十多個 Visitor,它們所做的事情類似於 選擇頁面做進一步分析、選擇不帶鏈接的頁面、列出“被鏈接最多”的頁面,等 等。因為所有這些操作都很簡單,所以 Visitor 模式(如清單 2 所示)可以工 作得很好,由於數據結構可以放到內存中,因此就算進行徹底搜索,花費也不是 很大:

清單 2. 用於 Web 快速搜索工具數據庫的 Visitor 模式

public interface Visitor {
   public void visitSite(Site site);
   public void visitLink(Link link);
}

噢,忘記報告了

如果不運行報告的話,Visitor 策略在訪問數據方面會做得非常好。使用數 據庫進行持久存儲的一個好處是:在生成報告時,SQL 的能力就會大放光彩 — — 幾乎可以讓數據庫做任何事情。甚至用 SQL 生成報告原型也很容易 —— 運 行原型報告,如果結果不是所需要的結果,那麼可以修改 SQL 查詢或者編寫新 的查詢,然後再試一試。如果改變的只是 SQL 查詢的話,那麼這個編輯-編譯- 運行周期可能很快。如果 SQL 不是存儲在程序中,那麼您甚至可以跳過這個周 期的編譯部分,這樣可以快速生成報告的原型。確定所需要的報告後,將它們構 建到應用程序中就很容易了。

因此,雖然對於添加新結果、尋找特定的結果和進行特殊傳輸來說,內存中 的數據結構都表現得很不錯,但是對於報告來說,這些變成了不利條件。對於所 有其自身結構與數據庫結構不同的報告,Visitor 都必須創建一個全新的數據結 構,以包含報告數據。因此,每一種報告類型都需要有自己的、特定於報告的中 間數據結構來存放結果,還需要一個用來填充中間數據結構的訪問者,以及用來 將中間數據結構轉換成最終報告的後處理(post-processing)代碼。似乎需要 做很多工作,尤其在大多數原型報告將被拋棄時。例如,假定您想要列出所有從 其他網站鏈接到某個給定網站的頁面的報告、所有外部頁面的列表報告,以及站 點上鏈接該頁面的那些頁面的列表,然後,根據鏈接的數量對報告進行歸類,鏈 接最多的頁面顯示在最前面。這個計劃基本上將數據結構從裡到外翻了個個兒。 為了用 Visitor 實現這種數據轉換,需要獲得從某個給定網站可以到達的外部 頁面鏈接的列表,並根據被鏈接的頁面對它們進行分類,如清單 3 所示:

清單 3. Visitor 列出被鏈接最多的頁面,以及鏈接到它們的頁面

public class InvertLinksVisitor {
   public Map<Page, Set<Page>> map = ...;

   public void visitLink(Link link) {
     if (link.linkFrom.site.equals(targetSite)
       && !link.linkTo.site.equals(targetSite)) {
       if (!map.containsKey(link.linkTo))
         map.put(link.linkTo, new HashSet<Page> ());
       map.get(link.linkTo).add(link.linkFrom);
     }
   }
}

清單 3 中的 Visitor 生成一個映射,將每一個外部頁面與鏈接它的一組內 部頁面相關聯。為了准備該報告,還必須根據關聯頁面的大小對這些條目進行分 類,然後創建報告。雖然沒有任何困難步驟,但是每一個報告需要的特定於報告 的代碼數量卻很多,因此快速報告原型就成為一個重要的目標(因為沒有提出報 告要求),試驗新報告的開銷比理想情況更高。許多報告需要多次傳遞數據,以 便對數據進行選擇、匯總和分類。

我的數據模型王國

這時,缺少一個正式的數據模型開始成為一項不利因素,該數據模型可以用 於描述收集的數據,並且可以用它更容易地表示選擇和聚合查詢。也許懶惰不像 開始希望的那樣有效。但是,雖然這個應用程序缺少正式數據模型,但也許我們 可以將數據存儲到內存中的數據庫,並憑借該數據庫進行查詢,通過這種方式借 用一個數據模型。有兩種可能會立即出現在您的腦海中:開源的內存中的 SQL 數據庫 HSQLDB 和 XQuery。我不需要數據庫提供的持久性,但是我確實需要查 詢語言。

HSQLDB 是一個用 Java 語言編寫的可嵌入的數據庫引擎。它既包含適用於內 存中表的表類型,又包含適用於基於磁盤的表的表類型,設計該引擎為了將表完 全嵌入到應用程序中,消除與大多數真實數據庫相關的管理開銷。要將數據裝載 到 HSQLDB,只需編寫一個 Visitor 即可,該 Visitor 將遍歷內存中的數據結 構,並為每一個將要存儲的實體生成相應的 INSERT 語句。然後可以對這個內存 中的數據庫表執行 SQL 查詢,以生成報告,並在完成這些操作後拋棄這個“數 據庫”。

噢,忘記了關系數據庫有多煩人

HSQLDB 方法是一個可行方法,但您很快就發現,我必須為對象關系的不匹配 而兩次(而不是一次)受罰 —— 一次是在將樹型結構數據庫轉換為關系數據模 型時,一次是在將平面關系查詢結果轉換成結構化的 XML 或者 HTML 結果集時 。此外,將 JDBC ResultSet 後處理為 DOM 表示形式的 XML 或者 HTML 文檔也 不是一項很容易的任務,需要為每一個報告提供一些定制的編碼。因此雖然內存 中的 SQL 數據庫 的確 可以簡化查詢,但是從數據庫中存入和取出數據所需要 的額外代碼會抵消所有節省的代碼。

讓 XQuery 來拯救您

另一個容易得到的數據查詢方法是 XQuery。XQuery 的優點是,它是為生成 XML 或者 HTML 文檔作為查詢結果而設計的,因此不需要對查詢結果進行後處理 。這種想法很有吸引力 —— 每個報告只有一層編碼,而不是兩層或者更多層。 因此第一項任務是構建一個表示整個數據集的 XML 文檔。設計一個簡單的 XML 數據模型和編寫遍歷數據結構,並將每一個元素附加到一個 DOM 文檔中的 Visitor 很簡單。(不需要寫出這個文檔。可以將它保持在內存中,用於查詢, 然後在完成查詢時丟棄它。當底層數據改變時,可以重新生成它。)之後,所有 要做的就是編寫 XQuery 查詢,該查詢將選擇並聚集用於報告的數據,並按最終 需要的格式(XML 或 HTML)對它們進行格式化。查詢可以存儲在單獨的文件中 ,以便進行快速原型制造,因此,可支持多種報告格式。使用 Saxon 評估查詢 的代碼如清單 4 中所示:

清單 4. 執行 XQuery 查詢並將結果序列化為 XML 或 HTML 文檔的代碼

String query = readFile(queryFile + ".xq");
  Configuration c = new Configuration();
  StaticQueryContext qp = new StaticQueryContext(c);
  XQueryExpression xe = qp.compileQuery(query);
  DynamicQueryContext dqc = new DynamicQueryContext(c);
  dqc.setContextNode(new DocumentWrapper(document, z.getName(), c));
  List result = xe.evaluate(dqc);
  FileOutputStream os = new FileOutputStream(fileName);
  XMLSerializer serializer = new XMLSerializer (os, format);
  serializer.asDOMSerializer();
  for(Iterator i = result.iterator(); i.hasNext(); ) {
    Object o = i.next();
    if (o instanceof Element)
      serializer.serialize((Element) o);
    else if (o instanceof Attr) {
      Element e = document.createElement("scalar");
      e.setTextContent(((Attr) o).getNodeValue());
      serializer.serialize(e);
    }
    else {
      Element e = document.createElement("scalar");
      e.setTextContent(o.toString());
      serializer.serialize(e);
    }
  }
  os.close();

表示數據庫的 XML 文檔的結構與內存中的數據結構稍有不同,每一個 <site> 元素都有嵌套的 <page> 元素,每一個 <page> 元 素都有嵌套的 <link> 元素,而每一個 <link> 元素都有 <link-to> 和 <link-from> 元素。實踐證明,這種表示方法對於 大多數報告都很方便。

清單 5 顯示了一個示例 XQuery 報告,這個報告處理鏈接的選擇、分類和表 示。它有幾個地方優於 Visitor 方法 —— 不僅代碼少(因為查詢語言支持選 擇、聚積和分類),而且所有報告的代碼 —— 選擇、聚積、分類和表示 —— 都在一個位置上。

清單 5.生成鏈接次數最多的頁面的完整報告的 XQuery 代碼

<html>
<head><title>被鏈接最多的頁面 </title></head>
<body>
<ul>
{
  let $links := //link[link-to/@siteUrl ne $targetSite
            and link-from/@siteUrl eq $targetSite]
  for $page in distinct-values($links/link-to/@url)
  let $linkingPages := $links[link-to/@url eq $page]/link- from/@url
  order by count($linkingPages)
  return
   <li>Page {$page}, {count($linkingPages)} links
   <ul> {
    for $p in $linkingPages return <li>Linked from {$p/@url}</li>
   }
   </ul></li>
}
</ul> </body> </html>

結束語

從開發成本角度看,XQuery 方法已證實可以節約大量成本。樹型結構對於構 建和搜索數據很理想,但對於報告,就不是很理想了。XML 方法很適合於報告( 因為可以利用 XQuery 的能力),但是對於整個應用程序的實現,該方法還有很 多不便,並會降低性能。因為數據集的大小是可管理的 —— 只有幾十兆字節, 所以可以將數據從一種格式轉換為從開發的角度看最方便的另一種格式。更大的 數據集,比如不能完全存儲到內存中的數據集,會要求整個應用程序都圍繞著一 個數據庫構建。雖然有許多處理數據持久性的好工具,但是它們需要的工作都比 簡單操縱內存中數據結構要多得多。如果數據集的大小合適,那麼就可以同時利 用這兩種方法的長處。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved