japan.internet.comThe Internet & IT Network
RSS
  • ニュース
  • コラム
  • リサーチ
  • ヘッドライン
  • 特集
  • ブログ
  • プレスリリース
  • 専門チャンネル
  • イベント
  • ランキング
  • ニュースメール
2009年7月4日
文字サイズ文字サイズ小文字サイズ中文字サイズ大
デベロッパー2007年4月3日 10:00

JDBC SQL/XMLの新機能を使ってXMLデータ処理を効率化する

海外海外internet.com発の記事
  • このエントリーを含むはてなブックマーク
  • この記事をクリップ!
  • Buzzurlにブックマーク
  • Yahoo!ブックマークに登録
  • newsing it!
  • この記事をokyuuへインポート

はじめに

 新バージョンのJava Database Connectivity API(JDBC 4)は、多数の素晴らしい機能によって大々的な化粧直しが施されています。おそらく、特に重要な新機能は、SQL 2003標準で規定されているXMLデータ型がサポートされたことでしょう。XMLをデータベースに格納してアプリケーションから更新するのは目新しいやり方でありませんが、SQL/XMLデータベースデータ型をサポートするマッピングインターフェイス(java.sql.SQLXML)がJDBCで提供されるのは今回が初めてです。この機能追加に伴い、java.sql.Connectionやjava.sql.ResultSetなど、他のインターフェイスもアップデートされたことは言うまでもありません。

 SQL 2003標準でXMLデータ型が導入される以前は、XMLをBLOB、CLOB、またはTEXT型として格納するしか方法がありませんでした。今日、多くのデータベースベンダーは既にXML型を製品の一部としてサポートしていますが、JDBC 4より前のJavaアプリケーションでは、データベース側のXMLデータ型とJDBCのサポートする型との間で変換を行う必要がありました。JDBCの新インターフェイスはXMLのためのJava固有のバインディングを定義しているので、データベース内のXMLデータを以前よりも簡単かつ効率的に処理できるようになりました。

 この記事では、JDBCの新しいインターフェイスでXMLデータ型を格納および取得する方法について解説します。また、サンプルのソースコードで具体的なやり方を示します。

XMLデータの格納と取得

 XMLデータをテーブル内のXML型の列に格納するには、まずjava.sql.Connection.createSQLXML()を呼び出します。これで、新たに導入されたjava.sql.SQLXML型のインスタンスが返されます。次に、このSQLXMLオブジェクトにXMLデータを追加するために、setOutputStream()setCharacterStream()、またはsetString(String xml)を呼び出す必要があります。この機能はBLOB型やCLOB型のサポートとよく似ていることに注意してください。

 新しいAPIの売りの1つは、java.sql.SQLXML上でsetResult(Class resultClass)を呼び出して、javax.xml.transform.Resultの実装クラス(DOMResult、JAXBResult、SAXResultなど)のインスタンスを取得できることです。つまり、特に変換しなくても以下のことが実現できます。

  1. XMLを取得する。
  2. そこからDOMResultを作成する。
  3. DOMResultをjava.sql.SQLXMLオブジェクトに割り当てる。
  4. XMLをjava.sql.Statementにバインドすることで、XMLをデータベースの列に直接格納する。

 java.sql.ResultSetからSQLXML型を取得するのは簡単です。通常の型でやっているように、列の名前かインデックスを指定してgetSQLXMLを呼び出すだけです。次に実際のXMLデータをjava.io.InputStream、java.io.Reader、または旧来のStringから取得します。具体的には、ResultSetから取得したjava.sql.SQLXMLインスタンス上でgetBinaryStream()getCharacterStream()、またはgetString()を呼び出します。同様にXMLを格納するには、SQLXMLインスタンス上でgetSource(Class sourceClass)を呼び出し、javax.xml.transform.Sourceを実装する任意のクラスからXMLデータに直接アクセスします。

サンプルプログラム

 JDBC 4が公式に発表されたのは2006年12月11日であるため、そのドライバを提供しているデータベースはごくわずかであり、現時点で完全に対応したデータベースは存在しません。この記事で紹介する例では、最新版のApache Derby 10.2を用いてXMLデータの格納とクエリを行っています(次ページの「リスト1 XMLの格納とクエリを行うサンプルプログラム」を参照)。しかし、Derbyはjava.sql.SQLXMLをまだサポートしていません。つまり、結果セットにXML値を直接バインドしたり、そこからXML値を直接取得したりすることはできません。この記事の目的からすると、これは大きな欠点のようにも見えますが、DerbyはSQL 2003に準拠し、埋め込みモードで簡単に使用できるので、将来的に完全対応のドライバが入手できた場合にXMLデータがどのようにして取得されるかを示すには十分です。

 DerbyのXML関連の演算子(XMLPARSE、XMLSERIALIZEなど)を利用すると、データを文字ストリームまたは文字列に変換してプログラムで使用できます。本稿の各サンプルタスクでは、SQL/XML完全対応のドライバを用いた場合のタスクの実現方法も示しています。実際、サンプルコード内の各タスクを、java.sql.SQLXMLを使用したコードに置き換えてもコードは正常にコンパイルされます。しかし、プログラムを実行すると、Derby固有のエラー(「XML値への直接バインドは許可されない」など)が発生します。要するに、このサンプルコードの主な目的は、SQL/XML対応のデータベースとどうやり取りすればよいかを示すことにあります。java.sql.SQLXMLを用いてサンプルプログラムと同じ処理を実行するコードも、また別の機会に紹介したいと思います。

 最初に、XMLデータ型を含む簡単なテーブルを作成します。

Statement s = c.createStatement();
s.execute("CREATE TABLE ARTICLE(ID INTEGER, DATA XML)");

XMLデータの挿入

 Derbyはjava.sql.SQLXML型をまだサポートしていないので、データをDATA列に挿入するときに、XMLとして解析可能な他の型へのバインドが必要になります。ここではCLOB型を使用しています。

ps = c.prepareStatement("INSERT INTO ARTICLE (ID, DATA) VALUES "
   + "(?, XMLPARSE (DOCUMENT CAST (? AS CLOB) PRESERVE "
   + "WHITESPACE))");
ps.setInt(1, id++);
ps.setClob(2, new StringReader(insert));

 さて、JDBC 4完全対応ドライバがある場合は、同じ処理をjava.io.Writerで実現できます(この変更を行ってもコードは正常にコンパイルされます)。

ps = c.prepareStatement(
            "INSERT INTO ARTICLE (ID, DATA) values (?, ?)");
SQLXML article = c.createSQLXML();
Writer writer = article.setCharacterStream();
writer.write(insert);
writer.close();
ps.setInt(1, id++);
ps.setSQLXML(2, article);

 あるいは、javax.xml.transform.dom.DOMSourceを使用する方法もあります。

ps = c.prepareStatement(
          "INSERT INTO ARTICLE (ID, DATA) values (?, ?)");
SQLXML article = c.createSQLXML();
DOMResult dom = (DOMResult)article.setResult(DOMResult.class);
dom.setNode(doc); // doc is instance of org.w3c.dom.Document
ps.setInt(1, id++);
ps.setSQLXML(2, article);

XMLデータの取得

 DerbyからXML型を取得するときは、以前と同様、XMLデータベース型を文字型に変換する必要があります。

ResultSet rs = s.executeQuery("SELECT XMLSERIALIZE (DATA AS CLOB) "
                              + "FROM ARTICLE WHERE ID = 2");

 java.sql.SQLXMLがサポートされている場合は、XMLデータベース型の列を選択するだけで同じタスクを実現できます。XMLデータを直接取得できるわけです。ここでは、結果セットから取得したXMLをDOMパーサーで評価するものとします。

PreparedStatement st 
  = c.prepareStatement("SELECT ID, DATA FROM ARTICLE");
ResultSet rs = st.executeQuery();

while (rs.next())
{
   SQLXML article = rs.getSQLXML("DATA");
   InputStream stream = article.getBinaryStream();
   DocumentBuilder parser =
   DocumentBuilderFactory.newInstance().newDocumentBuilder();
   Document doc = parser.parse(stream);
   // Do something...
}

 getBinaryStream()を呼び出す代わりにgetSource(Class sourceClass)を呼び出します。これでDOMSourceやSAXSourceなど、javax.xml.transform.Sourceを実装するクラスのインスタンスが返されます。

XMLEXISTSでXPathを判定

 最後の例では、SQL 2003の新しいXMLEXISTS述語でのXPathの使い方を示します。

Statement s = c.createStatement();
ResultSet rs = s.executeQuery("SELECT ID FROM ARTICLE WHERE "
   + "XMLEXISTS(’//author[text()="John Smith"]’ PASSING BY REF "
   + "DATA)");

 XMLQUERY関数を使用して任意のXQuery式を実行することもできます。しかし、XMLEXISTS述語とXMLQUERY関数は機能的にJDBC 4よりもSQL 2003寄りであるため、この記事ではこれ以上言及しません。

リスト1 XMLの格納とクエリを行うサンプルプログラム
import java.io.StringReader;
import java.sql.*;

public class XmlDbTester
{
   static final String XML1 =
      "<article>"
        +"<title>First Article</title>"
        +"<author>John Smith</author>"
        +"<body>A very short article.</body>"
    +"</article>";

   static final String XML2 =
      "<article>"
        +"<title>Second Article</title>"
        +"<author>Mary Jones</author>"
        +"<body>Another short article.</body>"
    +"</article>";

   static final String XML3 =
      "<article>"
        +"<title>Third Article</title>"
        +"<author>John Smith</author>"
        +"<body>Last short article.</body>"
    +"</article>";

   static final String[] ARTICLES = {XML1, XML2, XML3};

   public static void main(String s[])
   {
      XmlDbTester xdt = new XmlDbTester();
      Connection c = xdt.getConnection();
      xdt.loadDemoData(c);
      xdt.demoXmlResult(c);
      xdt.demoXPath(c);
      xdt.closeConnection(c);
      System.out.println("Done");
      System.exit(0);
   }
    
   void demoXmlResult(Connection c)
   {
      try
      {
         Statement s = c.createStatement();
         ResultSet rs 
          = s.executeQuery("SELECT XMLSERIALIZE (DATA AS CLOB) "
          + "FROM ARTICLE WHERE ID = 2");

         while(rs.next())
            System.out.println(
               "The article XML for article with ID = 2: 
"
               + rs.getString(1));

         s.close();
         rs.close();
      }
      catch(Exception e)
      {
         e.printStackTrace();
      }
   }

   void demoXPath(Connection c)
   {
      try
      {
         Statement s = c.createStatement();
         ResultSet rs
          = s.executeQuery("SELECT ID FROM ARTICLE WHERE "
        + "XMLEXISTS(’//author[text()="John Smith"]’ PASSING BY REF "
        + "DATA)");

         while(rs.next())
            System.out.println("John Smith wrote article with ID: "
                               + rs.getInt(1));

         s.close();
         rs.close();
      }
      catch(Exception e)
      {
         e.printStackTrace();
      }
   }
    
   void loadDemoData(Connection c)
   {
      try
      {
         Statement s = c.createStatement();
         s.execute("CREATE TABLE ARTICLE(ID INTEGER, DATA XML)");
         System.out.println("Created demo table: ARTICLE");
         s.close();

         PreparedStatement ps = null;
         int id = 1;

         for(String insert : ARTICLES)
         {
            ps = c.prepareStatement(
               "INSERT INTO ARTICLE (ID, DATA) VALUES "
               + "(?, XMLPARSE (DOCUMENT CAST (? AS CLOB) PRESERVE "
               + "WHITESPACE))");
            ps.setInt(1, id++);
            ps.setClob(2, new StringReader(insert));
            ps.executeUpdate();
         }

         System.out.println("Inserted test data into ARTICLE");

         if(ps != null )
            ps.close();
      }
      catch(SQLException e)
      {
         e.printStackTrace();
      }
   }

   Connection getConnection()
   {
      Connection c = null;

      try
      {
         c = DriverManager.getConnection(
                                  "jdbc:derby:XmlDemo;create=true");
         c.setAutoCommit(false);
      }
      catch (Exception e)
      {
         e.printStackTrace();
      }

      return c;
   }
    
   void closeConnection(Connection c)
   {
      try
      {
         c.close();
      }
      catch(Exception e) {}
   }
}

無限の可能性

 SQL/XMLのサポートでコードの見通しがよくなり、開発期間が短縮されます。通常、XMLデータをデータベースに格納するのは、それなりの事情があるからです。記事、イベントリスト、製品情報など、さまざまなメディアをオンラインで表示する最も一般的な手法の1つは、データ全体をXMLとして格納し、変換を経てそのデータをオンラインで表示するというものでしょう。java.sql.SQLXML APIドキュメントには、新しいデータ型からの変換方法が多数掲載されていますが、次の例を見てみましょう。これもJava SE 6ドキュメントに掲載されているものです。

File xsltFile = new File("transformer.xslt");
File xhtmlFile = new File("xhtml.xml");
Transformer xslt =
   TransformerFactory.newInstance().newTransformer(
     new StreamSource(xsltFile));
Source source = sqlxml.getSource(null);
Result result = new StreamResult(xhtmlFile);
xslt.transform(source, result);

 sqlxml変数は、データベースから取得したjava.sql.SQLXMLのインスタンスです。変換は必要ありません。データベースに格納されたXMLコンテンツを、わずか5行でXHTMLに変換できるというわけです。

著者紹介

Oliver Kaljuvee(Oliver Kaljuvee)
ソフトウェアコンサルタント。ニューヨーク市の金融企業のソフトウェア開発に従事。
関連テーマ
このエントリーを含むはてなブックマーク この記事をクリップ!
BuzzurlにブックマークBuzzurlにブックマーク Yahoo!ブックマークに登録
この記事をokyuuへインポート
最新トップニュース
データメーション
【データメーション】
中国が「Green Dam」フィルタ規制を撤回(7月1日)
Graphic Design Forum
【Graphic Design Forum】
Chris Dickman(6月25日)
プライバシー ジャパン・インターネットコム版
【プライバシー ジャパン・インターネットコム版】
グーグル・ストリートビューの問題について総務省の見解(6月23日)
エンジニアの独り言
【エンジニアの独り言】
システムを「使う」時代のエンジニアに求められるもの(6月2日)
最新ハイテク講座
最新ハイテク講座
電気は家庭でつくる時代へ!燃料電池「エネファーム」(7月3日)
アクセス解析で見るWebマーケティング
アクセス解析で見るWebマーケティング
決定力を探るアクセス解析(7月3日)
百式のネットビジネス研究
百式のネットビジネス研究
ファーストフードを高級っぽく盛り付けて紹介している「Fancy Fast Food」(7月3日)
週刊-サイト別アクセス状況データ
週刊-サイト別アクセス状況データ
ビデオリサーチインタラクティブ調査(月間インターネットオーディエンスデータ)(7月2日)
成約率、反応率を上げる Web 文章術
成約率、反応率を上げる Web 文章術
言葉がダイレクトにキャッシュを生む(7月2日)
不況時代の Web ビジネス最適化講座
不況時代の Web ビジネス最適化講座
アクセス解析エキスパートここだけの話、Web コンシェルジュの“勉強法”こっそり教えます(7月2日)
「Webからの脅威」―その傾向と最新対策
「Webからの脅威」―その傾向と最新対策
不正プログラムの分類(7月1日)
DevX
DevX
JavaScriptとDOMによる動的なWebページの作成(6月30日)
エンジニア転職ノウハウ開発室
エンジニア転職ノウハウ開発室
今のままで大丈夫?3匹の子ブタ的キャリア危険度診断(6月30日)
アイレップの SEM フロンティア
アイレップの SEM フロンティア
Web サイトは「無駄な穴のたくさん開いたじょうご」〜サイト成果向上の基本的な考え方(6月30日)
Copyright 2009 Japan Internet.com K.K. All Rights Reserved.http://www.internet.com/