■ ADO 2.5 と XML ■ ■ ADO 2.5 と XML
ADO 2.5 を利用すれば、粗所有るデータソースからのデータの読み取りや、ADO レコードセット(メモリ上に データを持つ仮想テーブルの様な物)へのデータの配置、XML へのデータ変換が可能に成る。XML に変換した データをデータストリームに配置すれば、ファイルや、ADO ASP Response オブジェクト(Web クライアント への情報の返送に使用する)、XML DOM と謂ったデータソースへの出力と仕て使用出来る様に成る。データを XML と仕て出力するには、COM の IStream インターフェイスを使用する。IStream インターフェイスは、ADO Stream オブジェクトへのデータの読み取りと書き込みをサポートする為に設計された物で有る。ADO 2.5 は IStream インターフェイスのサポートを要求する。
其の一方で、XML データは、ADO 2.5 の読み取り専用のレコードセットや読み取り・書き込みレコードセット のデータソースと仕て使用する事も出来る。例えば、Web サーバーの ASP ページに ADO 2.5 を使用すれば、デ ータをADO レコードセットに配置すると謂う方法で、データベースからデータを取得出来る様に成る。此のデ ータは、ASP Response オブジェクトへの XML 出力と仕て、ADO 2.0 を使って転送される。クライアントに到 着したXML データは、DHTML を使ってクライアントの ADO レコードセットに読み込まれる。クライアント 側でADO レコードセットと DHTML を使用すれば、ユーザーに依るデータの読み取りや更新が可能に成る。次 は、データをXML と仕て入出力する ADO 2.5 のサンプルを使って、其の仕組みを探って観る事にする。 ■ ADO 2.5 を使った XML と仕てのデータ出力
此のサンプルでは、SQL Server の Northwind データベースからデータを取り出し、其れを XML と仕てテキス トファイルに保存する。此の作業を実行する為、Visual Basic アプリケーションに ADO 2.5 を使用する。次の手 順に従って、サンプルアプリケーションを作成して観る事にする。
1.Visual Basic を開いて、標準 EXE アプリケーションを作成し、デフォルトフォームの名前を frmADOXML に変更する。
2.[プロジェクト]メニューから[参照設定]を選択し、[ Microsoft ActiveX Data Objects 2.5 Library ]へ の参照を追加する。
3.フォームにcmdSave と謂うコマンドボタンを追加し、キャプションに Save を設定する。 4.cmdSave コマンドボタンの Click イベントハンドラに、次のコードを追加する。
Private Sub cmdSave_Click()
Dim objNWRecordset As ADODB.Recordset Dim objNWConnection As ADODB.Connection Set objNWRecordset = New ADODB.Recordset Set objNWConnection = New ADODB.Connection objNWConnection.CursorLocation = adUseClient
' 次のステートメントの IES-FUJI を、適切なデータソースに置き換える必要が有る objNWConnection.Open "Provider=SQLOLEDB.1;" & _
"Integrated Security=SSPI; Persist Security Info=False;" & _ " User ID=sa; Initial Catalog=Northwind;
" Data Source=IES-FUJI"
objNWRecordset.CursorLocation = adUseClient
X
objNWRecordset.CursorType = adOpenStatic
Set objNWRecordset.ActiveConnection = objNWConnection objNWRecordset.Open "Products"
' レコードセットを XML と仕てファイルに保存する objNWRecordset.Save "C:¥Products.xml", adPersistXML End Sub
コードは先ず、objNWConnection と謂う ADO Connection オブジェクトと objNW … Recordset と謂う Recordset オブジェクトを生成し、オブジェクトのプロパティを設定してから、其等を開く。Connection オブジ ェクトは、データソースへの接続を提供する。Recordset オブジェクトは、データソースから取り出したデータ を持つ、メモリ上の仮想テーブルで有る。Recordset オブジェクトの CursorLocation プロパティは、データがク ライアントとサーバーの敦れに配置されるのかを決定する。亦、CursorLocation プロパティは、データベースと の接続を維持しなければ成らないのか(サーバーカーソル)、其れ共、切断されたレコードセットを作成して居る 間は接続を切断する事が出来るのか(クライアントカーソル)を決定する。ADO Connection オブジェクトの Open メソッドは、パラメータと仕て接続文字列を取る。此の接続文字列には、カタログ(使用するデータベー ス)、データソース(SQL Server の名前)、及び、ユーザーID(接続の確立時に使用する有効なユーザー名)が 含まれる。此の接続文字列は、SQL Server データベースに接続する。データソースの名前は、Northwind デー タベースを含んで居るSQL Server の名前に変更する必要が有る。
ADO Connection オブジェクトは、Northwind データベースに接続し、Recordset オブジェクトは、Northwind データベースのProducts テーブルに接続する。此等の接続が確立されたら、Recordset オブジェクトの Save メ ソッドを呼び出し、データをXML と仕て保存する。 コードが示す様に、Save メソッドはデータを XML と仕て保存する為に、パラメータ adPersistXML を使用す る。作成されるXML ファイルは、主に 2 つのセクションに分かれる。最初のセクションにはデータの BizTalk スキーマが、2 番目のセクションには実際のデータが含まれる。此のファイルには、4 つの名前空間プレフィッ クスが使用される。最初の名前空間プレフィックスは、データのスキーマ定義のプレフィックスに使用される s で有る。2 番目の名前空間プレフィックスは、スキーマのデータ型定義に使用される dt で有る。3 番目の名前空 間プレフィックスは、ADO レコードセットのプロパティとメソッドを参照する rs で有る。然して、4 番目の名 前空間プレフィックスは、実際のデータを参照するz で有る。 此のコードは、次のProducts.xml ファイルを生成する。 <xml xmlns:s='uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882' xmlns:dt='uuid:C2F41010-65B3-11d1-A29F-00AA00C14882' xmlns:rs='urn:schemas-microsoft-com:rowset' xmlns:z='#RowsetSchema'> <s:Schema id='RowsetSchema'>
<s:ElementType name='row' content='eltOnly'> <s:AttributeType name='ProductID' rs:number='1'> <s:datatype dt:type='int' dt:maxLength='4'
rs:precision='10' rs:fixedlength='true' rs:maybenull='false'/>
</s:AttributeType>
<s:AttributeType name='ProductName' rs:number='2' s:writeunknown='true'>
<s:datatype dt:type='string' dt:maxLength='40' rs:maybenull='false'/>
<s:AttributeType name='SupplierID' rs:number='3' rs:nullable='true' rs:writeunknown='true'>
<s:datatype dt:type='int' dt:maxLength='4' rs:precision='10' rs:fixedlength='true'/>
</s:AttributeType>
<s:AttributeType name='CategoryID' rs:number='4' rs:nullable='true' rs:writeunknown='true'>
<s:datatype dt:type='int' dt:maxLength='4' rs:precision='10' rs:fixedlength='true'/>
</s:AttributeType>
<s:AttributeType name='QuantityPerUnit' rs:number='5' rs:nullable='true' rs:writeunknown='true'>
<s:datatype dt:type='string' dt:maxLength='20'/> </s:AttributeType>
<s:AttributeType name='UnitPrice' rs:number='6' rs:nullable='true' rs:writeunknown='true'> <s:datatype dt:type='i8' rs:dbtype='currency' dt:maxLength='8' rs:precision='19'
rs:fixedlength='true'/> </s:AttributeType>
<s:AttributeType name='UnitsInStock' rs:number='7' rs:nullable='true' rs:writeunknown='true'>
<s:datatype dt:type='i2' dt:maxLength='2' rs:precision='5' rs:fixedlength='true'/>
</s:AttributeType>
<s:AttributeType name='UnitsOnOrder' rs:number='8' rs:nullable='true' rs:writeunknown='true'>
<s:datatype dt:type='i2' dt:maxLength='2' rs:precision='5' rs:fixedlength='true'/>
</s:AttributeType>
<s:AttributeType name='ReorderLevel' rs:number='9' rs:nullable='true' rs:writeunknown='true'>
<s:datatype dt:type='i2' dt:maxLength='2' rs:precision='5' rs:fixedlength='true'/> </s:AttributeType>
<s:AttributeType name='Discontinued' rs:number='10' rs:writeunknown='true'>
<s:datatype dt:type='boolean' dt:maxLength='2' rs:fixedlength='true' rs:maybenull='false'/> </s:AttributeType> <s:extends type='rs:rowbase'/> </s:ElementType> </s:Schema> <rs:data>
<z:row ProductID='1' ProductName='Chai' SupplierID='1' CategoryID='1' QuantityPerUnit='10 boxes x 20 bags' UnitPrice='18' UnitsInStock='39' UnitsOnOrder='0' ReorderLevel='10' Discontinued='False'/>
<z:row ProductID='2' ProductName='Chang' SupplierID='1' CategoryID='1' QuantityPerUnit='24 -12 oz bottles' UnitPrice='19' UnitsInStock='17' UnitsOnOrder='40' ReorderLevel='25' Discontinued='False'/>
...
</rs:data> </xml>
ADO 2.5 を使用する事で、Northwind データベースの Products テーブルのスキーマとデータから成る、XML ドキュメントが完成した。
※ ADO 2.5 の詳細に付いては、Microsoft の Web サイトで MDAC (Microsoft Data Access Components ) 2.5 SDK を参照され度い。
生成したデータをXML と仕て使用する為には、データを変更しなければ成らない場合が有る。例えば、列名に スペースと謂った無効な文字が使用されて居る場合は、有効なXML 名に変更しなければ成らない。此の場合は name 属性を変更し、フィールドの元の名前を持つ rs:name 属性を追加する。例えば、データベースに ShipperName と謂う列が存在する場合、AttributeType は次の様に成る。
<s:AttributeType name='Shipper Name' rs:number='9' rs:nullable='true' rs:writeunknown='true'>
<s:datatype dt:type='i2' dt:maxLength='2' rs:precision='50' rs:fixedlength='true'/>
</s:AttributeType>
此のAttributeType を、次の様に変更しなければ成らない。 <s:AttributeType name='ShipperName' rs:name='Shipper Name' rs:number='9' rs:nullable='true' rs:writeunknown='true'> <s:datatype dt:type='i2' dt:maxLength='2' rs:precision='50' rs:fixedlength='true'/> </s:AttributeType> 此のドキュメントは、Internet Explorer 5 に XML と仕て表示するか、XSL ドキュメントを使って XHTML と 謂ったフォーマットに変換する事が出来る。 生成されるオリジナルファイルは、読み取り専用のADO レコードセットの生成にしか利用出来ない。クライア ント側に更新可能な切断されたレコードセットを生成し度い場合は、ElementType 定義に rs:updatable 属性を 追加しなければ成らない。クライアント側の切断されたレコードセットは、元のデータソースへの接続を持たな い。ユーザーは、レコードセットの参照や、編集、削除、更新、レコードの追加を行う事が出来るが、変更をデ ータベースに保存するには、データベースへの接続を再確立しなければ成らない。変更を保存するには、切断さ れたレコードセットをデータベースに再接続した後に、ADO レコードセットの UpdateBatch メソッドを呼び出 す。UpdateBatch は、1 つの呼び出しで複数のレコードセット更新をサーバーに送信するメソッドで有る。 データを更新可能にするには、ElementType の定義を次の様に変更する。
■ ADO 2.5 を使った XML と仕てのデータ入力
ADO 2.5 が生成する XML データの読み取りは、データの出力と同じ様に簡単だ。次は、ADO 2.5 を使って、サ ンプルアプリケーションにデータを入力する方法に付いて観る事にする。frmADOXML フォームに新しいコマ ンドボタンを追加し、名前をcmdRetrieve に変更し、キャプションに Retrive&Add を設定する。cmdRetrieve コマンドボタンのClick イベントハンドラに、次のコードを追加する。
Private Sub cmdRetrieve_Click()
Dim objNWRecordset As ADODB.Recordset Set objNWRecordset = New ADODB.Recordset ' XML ファイルにレコードセットを開く
objNWRecordset.Open "C:¥Products.XML", Options:=adCmdFile ' 新しいレコードセットを追加する objNWRecordset.AddNew objNWRecordset.Fields("ProductName") = "Test" End Sub ※ 此のコードを実行する前に、生成された XML ファイルのスキーマセクションに rs:updatable='true' 属性を 追加しないと、エラーが発生する。
Options を adCmdFile に設定すると、ADO は、データベースの代わりに通常のファイルからデータを取り出す 物と認識する。 新しいレコードの追加や、レコードの編集、又は、レコードの削除を行ったら、ADO レコードセットの Update メソッドを呼び出さなければ成らない。Update メソッドを呼び出す度に、レコードセットの更新されたレコー ドにマークが付けられる。実際の変更を参照するには、レコードセットを変更し、変更をファイルに保存する。 データベースに接続して居ない為、此等の変更はレコードセット丈に適用され、データベースの実際のデータに は適用されない。 データの変更 此れで、データをXML と仕て保存し、ADO を使って XML データを開く方法が解った。次は、XML データを 変更する方法に付いて調べて観る事にする。先ず、作成したProducts.xml ファイルを開く為のコードを記述し、 次にデータを変更する。データを変更したら、レコードセットのUpdate メソッドを呼び出す。変更が完了した ら、ProductsUpdate.xml と謂うファイルに新しいデータを保存する。先ず、フォームに cmdMakeChanges と 謂う新しいコマンドボタンを追加し、キャプションにMake Changes を設定する。cmdMakeChanges コマンド ボタンのClick イベントハンドラに、次のコードを追加する。
Private Sub cmdMakeChanges_Click() Dim objNWRecordset As ADODB.Recordset Set objNWRecordset = New ADODB.Recordset ' XML ファイルにレコードセットを開く
objNWRecordset.Open "C:¥Products.XML", Options:=adCmdFile objNWRecordset.Fields("ProductName") = "Test"
objNWRecordset.Update objNWRecordset.MoveLast
objNWRecordset.Delete objNWRecordset.Update
objNWRecordset.Filter = adFilterPendingRecords
objNWRecordset.Save "c:¥ProductsUpdate.xml", adPersistXML End Sub
先ず、最初のレコードの製品名を Test に変更し、最後のレコードを削除する。次に、変更したレコードを ProductUpdate.xml ファイルに保存する。[ Make Changes ]コマンドボタンをクリックすると、 ProductUpdate.xml ファイルが次の様に変更されて居るのがわかる。
...
<rs:update> <rs:original>
<z:row ProductID='1' ProductName='Chai' SupplierID='1' CategoryID='1' QuantityPerUnit='10 boxes x 20 bags' UnitPrice='18' UnitsInStock='39' UnitsOnOrder='0' ReorderLevel='10' Discontinued='False'/> </rs:original> <z:row ProductName='Test'/> </rs:update> ... <rs:delete>
<z:row ProductID='77' ProductName='Original Frankfurter grune Sose' SupplierID='12' CategoryID='2' QuantityPerUnit='12 boxes'
UnitPrice='13' UnitsInStock='32' UnitsOnOrder='0' ReorderLevel='15' Discontinued='False'/> </rs:delete> ADO は更新された XML ファイルを使って、編集されたフィールドの元の値と新しい値を持つレコードセットを 再構築し、削除された行を突き止める。次は、データソースを更新する方法に付いて調べて観る事にする。 ■ データソースの更新データソースの更新 ■ 此れ迄に操作したデータは、ローカルファイルにしか保存されて居ない。此のデータを元のデータソース(此の 場合は Northwind データベース)に保存しなければ成らない。更新されたデータを保存する為、フォームに cmdSaveUpdate と謂う新しいコマンドボタンを追加し、キャプションに Save Update を設定する。 cmdSaveUpdate コマンドボタンの Click イベントハンドラに、次のコードを追加する。
Private Sub cmdSaveUpdate_Click() Dim objNWRecordset As ADODB.Recordset Dim objNWConnection As ADODB.Connection Dim objXMLRecordset As ADODB.Recordset Dim lngFieldCounter As Long
Set objNWRecordset = New ADODB.Recordset Set objXMLRecordset = New ADODB.Recordset
Set objNWConnection = New ADODB.Connection objNWConnection.CursorLocation = adUseServer ' 次のステートメントの IES-FUJI を、
' 適切なデータソースに置き換える必要がある objNWConnection.Open _
"Provider=SQLOLEDB.1; Integrated Security=SSPI;" & _ "Persist Security Info=False;" & _
"User ID=sa; Initial Catalog=Northwind; Data Source=IES-FUJI" objNWRecordset.CursorLocation = adUseServer
objNWRecordset.CursorType = adOpenDynamic objNWRecordset.LockType = adLockPessimistic
Set objNWRecordset.ActiveConnection = objNWConnection
objXMLRecordset.Open "C:¥ProductsUpdate.XML", Options:=adCmdFile objXMLRecordset.Filter = adFilterPendingRecords
Do Until objXMLRecordset.EOF
If objXMLRecordset.EditMode<>adEditAdd Then objNWRecordset.Open _
"Select * From Products Where ProductID=" _
& objXMLRecordset.Fields.Item("ProductID").OriginalValue If objXMLRecordset.EditMode = adEditDelete Then
' 削除
objNWRecordset.Delete Else
' 編集
For lngFieldCounter = 0 To objXMLRecordset.Fields.Count-1 ' 主キーは変更不可能 If UCase(objXMLRecordset.Fields.Item(lngFieldCounter).Name) _ <>"PRODUCTID " Then objNWRecordset.Fields.Item(lngFieldCounter).Value =_ objXMLRecordset.Fields.Item(lngFieldCounter).Value End If Next End If Else objNWRecordset.Open _
"Select * From Products Where ProductID=" & 0 objNWRecordset.AddNew
' 新しいレコードの追加
For lngFieldCounter=0 To objXMLRecordset.Fields.Count -1 ' productID の自動インクリメントフィールド If UCase(objXMLRecordset.Fields.Item(lngFieldCounter).Name) _ <>"PRODUCTID " Then objNWRecordset.Fields.Item(lngFieldCounter).Value = _ objXMLRecordset.Fields.Item(lngFieldCounter).Value End If Next End If
objNWRecordset.Update objNWRecordset.Close objXMLRecordset.MoveNext Loop
End Sub
此の場合も、Northwind データベースに接続する為に、objNWConnection と謂う Connection オブジェクトを 生成する。亦、ProductsUpdate.xml ファイルのデータを保持する為に、objNWRecordset と謂う Recordset オ ブジェクトを生成する。先程のサンプルと同様に、此の場合もデータソースを構成して、接続文字列を変更しな ければ成らない。objNWRecordset は、WHERE 句を持つ SELECT ステートメントを使って、更新するレコー ドへの参照を取得するのに使用する。 objXMLRecordset と謂うもう 1 つの Recordset オブジェクトは、追加、編集、又は、削除されたデータを持つ、 XML データの取り出しに使用する。亦、ADO データストリームから XML データを取り出す事も出来る。 objXMLRecordset レコードセットに保存されて居た XML データを取得したら、変更されたレコードと新しいレ コード丈が検出される様に、フィルタを適用する。次に、objXMLRecordset の新しいレコード、又は、変更さ れたレコードを1 件宛取り出し、objNWRecordset を使って、データベースから其のレコードを取り出す。 変更の対象と成るレコード而巳を集めた後は、AddNew、Delete、又は、Edit の内の、正しい操作を実行する事 が出来る。先ず、objXMLRecordset の EditMode プロパティを検査し、其のレコードに実行された操作を突き 止め、正しい操作を実行する。
切断されたADO レコードセットを使った経験が有れば、ADO レコードセットの UpdateBatch メソッドを使用 すると思ったかも知れない。残念乍ら、XML を使って作成される切断された ADO レコードセットは、データを 取り出した元のテーブルへの参照を持たない。此の為、正しいデータベースにADO 接続を確立し、レコードセ ットのActiveConnection プロパティに此の接続を設定したと仕ても、レコードセットを正しいテーブルに接続 する手段が無い。レコードセットを正しいテーブルに接続出来ない為、UpdateBatch メソッドは機能しない。先 程のサンプルからも解る様に、此処では、objXMLRecordset 及び objNWRecordset と謂う、2 つの Recordset オブジェクトを生成した。
※ コードを簡潔に保つ為、此の章のサンプルにはエラー処理が含まれて居ない。何のプロダクションコードにも、 エラーハンドラは不可欠で有る。此のサンプルでは、変更の対象と成るレコードがデータベースに存在する事、 他のユーザーに依って変更されて居ない事を確認しなければ成らない。データの状態を確認するには、フィー ルド毎にRecordset オブジェクトの PreviousValue プロパティを使用する。PreviousValue プロパティは、 変更される前のフィールドの値を提供する。サンプルのコードには、テーブル名とフィールド名を含んだクエ リ文字列が使用されて居る。但し、プロダクションコードでは、テーブル名やフィールド名が変更された場合 に定数の値を変更する丈で済む様に、クエリ文字列には定数を使用する。
■ ストリームの処理
先程のサンプルでは、情報をデータファイルに保存した。此れは、殆どのアプリケーションに取って不適切で有 る可能性が高い。此れに変わる方法は、データをDOM オブジェクトに渡したり、データを ASP Response オブ ジェクトでクライアントに返す為の、データストリームを直接操作する事だ。次は、ストリームを使って、デー タを直接DOM オブジェクトに挿入するサンプルを観る事にする。
此のサンプルは、テキストファイルからデータをロードし、其れをADO Stream オブジェクトに配置する。次 に、データをDOM オブジェクトにロードする。DOM オブジェクトにロードしたデータは、XSL を使った変換
を含め、粗自由に操作する事が出来る。データを ADO Stream オブジェクトに配置する為、フォームに cmdStream と謂う新しいコマンドボタンを追加し、キャプションに Stream を設定する。cmdStream コマンド ボタンのClick イベントハンドラに、次のコードを追加する。
Private Sub cmdStream _Click()
Dim objNWRecordset As ADODB.Recordset Dim objADOStream As ADODB.Stream Dim objDOM As DOMDocument Dim strXML As String
Dim objNWConnection As ADODB.Connection Set objADOStream = New ADODB.Stream Set objDOM = New DOMDocument
Set objNWRecordset = New ADODB.Recordset Set objNWConnection = New ADODB.Connection objNWConnection.CursorLocation = adUseClient ' 次のステートメントの IES-FUJI を、
' 適切なデータソースに置き換える必要が有る objNWConnection.Open _
"Provider=SQLOLEDB.1; Integrated Security=SSPI;" & _ "Persist Security Info=False;" & _
"User ID=sa; Initial Catalog=Northwind; Data Source=IES-FUJI" objNWRecordset.CursorLocation = adUseClient
objNWRecordset.CursorType = adOpenStatic
Set objNWRecordset.ActiveConnection = objNWConnection objNWRecordset.Open "Products" objADOStream.Open objNWRecordset.Save objADOStream,adPersistXML strXML = objADOStream.ReadText objDOM.loadXML strXML End Sub
他のサンプルと同様に、此のコードはADO Connection オブジェクトと Recordset オブジェクトを生成する。 Northwind Traders データベースへの接続を確立し、Products テーブルのデータをレコードセットに配置した ら、objADOStream と謂う ADO Stream オブジェクトに XML データを保存する。ADO Stream オブジェクト の情報を strXML と謂う文字列変数に代入し、ドキュメントオブジェクトの loadXML メソッドを使って、 objDOM と謂う DOM ドキュメントオブジェクトに XML データを配置する。要するに、通常のデータベースの データを開いて、其れをXML と仕て DOM ドキュメントオブジェクトに渡して居る。此の様に、ADO レコー ドセットは揮発性で有り、クライアントとサーバーの両方で、分散アプリケーションの強力なデータアクセスコ ンポーネントの構築に利用する事が出来る。