この特集のトップページへ
>
Chapter 6:ビジネスロジックの設計
6.2.4 顧客一覧の取得
●配列データを返す4つの方法
3つ目の方法は,結果を文字列としてクライアントに返す方法である。どのような文字列として返してもよいが,クライアント側でその文字列を再構成し,表形式のそれぞれの値に分割できることが望ましい。そこで考えられるのは,XMLの利用である。
XMLとは,乱暴ないい方をすれば,任意のエレメント(HTMLで俗に呼ばれる「タグ」のこと)を利用できる言語である。エレメントのあいだに情報を含めることで,それを受け取ったクライアントは,自由にその情報を加工できる。たとえば,Table 6-8のような表形式のデータをXMLで表現する場合,次のようになる。
<CUSTOMERS>
<CUSTOMER>
<CUSTOMERNAME>ソフトバンクパブリッシング</CUSTOMERNAME>
<TELEPHONE>03-1234-5678</TELEPHONE>
<FAX>03-1234-5679</FAX>
</CUSTOMER>
<CUSTOMER>
<CUSTOMERNAME>マイクロソフト</CUSTOMERNAME>
<TELEPHONE>03-2345-6789</TELEPHONE>
<FAX>03-2345-6790</FAX>
</CUSTOMER>
</CUSTOMERS>
ここでは,1行分のデータを<CUSTOMER></CUSTOMER>で括り,そのなかに,名前を<CUSTOMERNAME></CUSTOMERNAME>で,電話番号を<TELEPHONE></TELEPHONE>で,FAX番号を<FAX></FAX>で括り,さらに全体を<CUSTOMERS></CUSTOMERS>でまとめてある。実際には,どのようなエレメント名を使ってもかまわない(エレメント名として日本語を利用することもできる)。ただし,XMLでは,必ず1つのエレメントが最上位に位置していなければならないことになっており,最上位となっているエレメントのことを「ルートエレメント」と呼ぶ。上の例では,全体を括っている<CUSTOMERS></CUSTOMERS>がそれに相当する。ルートエレメントが存在しないものや複数のルートエレメントを備えたものは,XMLデータとしては認められない。上記の例の場合,<CUSTOMERS>と</CUSTOMERS>を省いてしまうと,最上位に<CUSTOMER></CUSTOMER>が2つ存在することになるから,XMLデータとしては認められないのである。
XML形式の文字列として結果を返すメソッドの実装例は,List 6-40のようになる。
List 6-40は,単純に文字列を結合してそれを戻り値としているわけだが,XML形式の文字列を生成するには,もう1つ方法がある。それはXMLパーサーを使う方法である。XMLパーサーとは,XMLにかかわる一連の操作をするためのライブラリである。Microsoft社はXMLパーサーをCOMコンポーネントとして提供しており,それを使うことでVisual BasicやVisual C++,VBScript,JScriptなどからXMLで表記された文字列(またはXMLで記述されたファイルを直接開く)を操作することができる。Microsoft社のXMLパーサーはInternet Explorer 5.0以降やWindows 2000に付属している。また,XPath(XMLでリンクを表記するための仕様)やXSLT(XML文章をほかの形式に変換するための仕様)に対応した新しいXMLパーサーのTechnology Priview版をMSDNのページからダウンロードすることができる。
/* 日本語版があったようなきもするだけれど,気のせい? 少なくとも,日本語のページはあったような? http://www.microsoft.com/japan/developer/
downloads/webtechnology/xml/msxml.aspでもよいが,リンク先を変更するか
XMLパーサーを使ってList 6-40と同等の処理を実装すると,List 6-41のようになる。
XMLパーサーを使う場合には,Visual Basicで[Microsoft XML Version 2.0]というライブラリファイルを参照設定しなければならない。
XMLについては,連載中であとから扱う予定である。そのなかで,XMLパーサーの詳細も説明する。よって,ここではXMLパーサーに搭載されている各メソッドの意味など,詳細な利用方法については割愛する。
List 6-41は,単なる文字列処理として実装したList 6-40に比べて長く,また処理も複雑であるため,わざわざXMLパーサーを使う必要はないと感じられるかもしれない。しかし,XMLパーサーを使うと,XMLツリー構造を直接オブジェクトとして操作することができるため,特定のエレメントのあいだに別のエレメントを挿入したり,特定のエレメントだけを削除したりといった複雑な処理は簡単になる。また,XMLパーサーで処理させると,XMLに構成上のミス(エレメントの抜けなど)がないかどうかをチェックできるため,そういった意味でも優れている。
XMLパーサーは,W3Cが規定しているDOM(Document Object Model)に沿って実装されている。DOMの仕様では,XMLパーサーはXMLの構成上の問題点を必ずしもエラーとして指摘しなくてもよいことになっている。ここでいう構成上の問題とは,単なるエレメントを示すタグの食い違いがある場合(タグが閉じていないなど)だけでなく,DTD(Document Type Definition:文章構造型定義)に違反していないかどうかという調査も含まれる。Microsoft社が提供しているXMLパーサーはDTD違反も調査するが,すべてのXMLパーサーがDTDとの比較をサポートしているわけではない。
さて,List 6-40やList 6-41に示したGetCustomers3メソッドからの戻り値を読み取り,各エレメント別にデータを取得するためには,Visual Basicの文字列操作関数(Right関数,Mid関数,Left関数など)を使って処理してもよいが,それよりもXMLパーサーを使ったほうが手軽である。XMLパーサーを使えば,XMLデータを読み込み,エレメントごとに分解して容易に処理することができるためである。XMLパーサーを用いてXML文字列を分解する例をList 6-42に示す。
XMLを使ってデータを転送するようにすれば,結果を文字列として扱うことができるので,可視性に優れるというメリットがある。また,XMLは比較的単純な構造で,しかも,さまざまな言語やOS用のXMLパーサーが提供されているため,異機種間での連携には積極的に使いたい技術の1つである。
ADO 2.5では,XMLファイルの読み書きがサポートされている。よって,XMLファイルをADOコンポーネントで開いてデータベースのように扱ったり,リレーショナルデータベースから取得したデータをXMLファイルとして書き出したりすることができるようになった。そういった意味では,XMLを操作するのに必ずしもXMLパーサーを使う必然性はない。
4)ADODB.Recordsetオブジェクトとして返す
最後の方法は,ADODB.Recordsetオブジェクトとしてクライアントに返す方法である。これは,データベーステーブルの結果を返すのに便利である。たとえば,List 6-43のように実装する。
List 6-43は,ローカルのSQL ServerやMSDE(6行目の“Driver=SQL Server;Server=(local)”)のbusinesssampleDBデータベースに接続し,“SELECT * FROM 顧客情報”というSQL文を実行して得たADODB.Recordsetオブジェクトを返すことを想定している。
List 6-43で示したGetCustomers4メソッドを呼び出して,取得したADODB.Recordsetオブジェクトの内容を表示するプログラムは,たとえばList 6-44のようになる。
List 6-44では,7〜9行目でフィールド名がNAME,TELEPHONE,FAXのデータを取得している。もちろん,フィールド名は,List 6-43で開いたデータベーステーブルの構造に依存する。
ところで,List 6-43とList 6-44の組み合わせは,COM+での利用を考えたときには少々都合が悪い。List 6-43をビジネスロジックとして実装してCOM+に登録し,List 6-44をプレゼンテーション層として利用したときには,パフォーマンス上の問題が生じる。
その理由は,List 6-43がADODB.Recordsetオブジェクトをデータベースと接続したままクライアント(プレゼンテーション層)に返し,そのADODB.RecordsetオブジェクトのCloseメソッドをクライアントから呼び出すことによって閉じている(List 6-44の15行目)という点にある。この方法では,万一,クライアントがGetCustomers4メソッドを呼び出したあとでADODB.RecordsetオブジェクトのCloseメソッドを呼び出さなかった場合には,サーバー側でずっとデータベースコネクションが張られたままになってしまう(Fig.6-45)。
Fig.6-45 取得したADODB.Recordsetオブジェクトを閉じなかった場合

COM+には,リソースディスペンサの機能があるため,データベースコネクションを有効に使い回すために使うときだけデータベースに接続し,データベースの利用が終わったら即座にデータベースコネクションを閉じることがよしとされる。Fig.6-45に示した形態では,データベースコネクションが閉ざされるタイミングがクライアント側次第となってしまうため,場合によってはデータベース接続のパフォーマンス劣化につながることもある。
Fig.6-45を見るとわかるように,クライアント側で取得したADODB.Recordsetオブジェクトはデータベースに接続されたままである。したがって,List 6-43の6行目で読み取り専用(adLockReadOnly)ではなく書き込み可能なものとして(adLockPessimisticまたはadLockOptimistic)指定することで,クライアントはそのADODB.Recordsetオブジェクトを通じて,データベースに書き込むこともできるようになる。しかし,N階層モデルにおいてそのような設計をすると,ビジネスロジックを経由せずに直接プレゼンテーション層がデータストア層にアクセスできることになる。その場合,パフォーマンス面だけでなく,セキュリティ面でも好ましくない。ビジネスロジックがADODB.Recordsetオブジェクトを返す場合には,それを戻り値として得たクライアントから不正にデータベースに書き込めないようにするため,読み取り専用のADODB.Recordsetオブジェクトを返すべきである。
そこで,クライアントにADODB.Recordsetオブジェクトを返すときには,データベースコネクションを切ったものを渡すようにする。これは単純にADODB.RecordsetオブジェクトのCloseメソッドを呼び出してからクライアントに戻すというのとは意味が違う。ADODB.RecordsetオブジェクトはCloseメソッドを呼び出した時点で保持しているフィールドの内容を破棄してしまうため,単にCloseメソッドを呼び出すだけでは,呼び出し元にデータの入ったADODB.Recordsetが渡されず,常に中身が空のADODB.Recordsetオブジェクトが渡されてしまう。
ではどのようにするかというと,List 6-45に示すように,クライアント側のカーソルを指定してレコードセットを開き,SQL文で操作したら,ActiveConnectionプロパティにNothingを格納するのである。こうすることによって,データベースと接続していないADODB.Recordsetオブジェクトをクライアント側に戻すことができる。
List 6-45においてポイントとなるのが7行目と13行目である。
7行目では,CursorLocationプロパティをadUseClient定数にして,クライアント側のカーソルを指定している。CursorLocationプロパティのデフォルト値はadUseServer定数である。adUseServer定数は,サーバー側のカーソルを利用することを意味する。カーソルとは,データベーステーブルの行位置を示すものである。サーバー側のカーソルを利用する場合には,データベースと接続していないとカーソルを動かすことができないため,ADODB.Recordsetオブジェクトがデータを保持しているあいだはデータベースと常に接続している必要がある。つまり,CursorLocationプロパティがadUseServer定数に設定されている場合(adUseServer定数はデフォルト値なので明示的にCursolLocationプロパティを設定していない場合も含む)には,ADODB.Recordsetオブジェクトがデータを保持しているあいだは,データベースとの接続を切ることができない。なお,CursolLocationプロパティの設定は,後続するデータベース接続に反映され,すでに接続されているものについては反映されない。よって,10行目にあるOpenメソッドのまえにCursorLocationプロパティを設定しなければならない(7行目と10行目を入れ替えてはいけない)。
CursorLocationプロパティをadUseClient定数に設定した場合には,カーソルタイプ(Openメソッドの第3引数またはADODB.RecordsetオブジェクトのCursorTypeプロパティ)には,adOpenStatic(静的カーソル)しか設定できない。そして,ロックタイプ(Openメソッドの第4引数またはADODB.RecordsetオブジェクトのLockTypeプロパティ)にadLockPessimistic(レコードごとの排他的ロック)を設定できない。
13行目では,ActiveConnectionプロパティの値をNothingに設定している。これによって,データベースとの接続が切断される。COM+で管理されているCOMコンポーネントとして動作している場合には,この時点でデータベース接続がリソースディスペンサに戻される。ただし,ActiveConnectionプロパティの値を変更できるのは,CursolLocationプロパティがadUseClientに設定されている場合,もしくは,ADODB.Recordsetオブジェクトが閉じている(Openメソッドを呼び出していないかCloseメソッドを呼び出して閉じた)場合だけである。そのため,7行目でCursorLocationプロパティを設定しなかった場合には,13行目で実行時エラーが生じる。
以上の処理によって,データベースに接続されていないADODB.Recordsetオブジェクトを戻り値として利用することができるようになる。
| Chapter 6 30/92 |
