バージョンアップされた
oo4o

初音 玲 HATSUNE, Akira


 Oracleを使うWindowsアプリケーションにCOM対応の言語を使える開発者は,幸せだ.それは,Oracle Objects for OLEという安定したミドルウェアが使えるからだ.
 そのOracle Objects for OLEもバージョンアップを重ね,現在では,V2.3にまで成長してきた.今回は,バージョンごとの機能差を紹介すると共に,最新版の新機能の使い方を探ってみたいと思う.

Oracle Objects for OLEの基礎知識

 Oracle Objects for OLEは,Visual BasicとCOMインターフェイスで接続するActiveX DLLだ.よって,Visual Basic以外にもExcel,WordまたはASPなどからも利用することができる.また,Oracle Objects for OLEは,図1に示すように極めてシンプルな構造になっており,Oracle7やOracle8の機能をほとんどそのまま利用できる透過性を兼ね備えている.
 ただし,残念なことは,Orace8のオブジェクト型に対応したOracle Objects for OLEオブジェクトは,まだ存在しない.「オブジェクト」という言葉が羅列されてわかりづらいかもしれないが,たとえば,UPDATE SQLで操作することはできるが,OraDynasetオブジェクトを作成して,そのメソッドとして,Oracle8のオブジェクト型を操作することはできないということだ.次の版がVer3になるかVer2.4になるかは不明だか,近い将来の対応版を待つしかない.

図1:Oracleが提供するミドルウェアの構造
図1:Oracleが提供するミドルウェアの構造

Oracle Objects for OLE歴史探求

小見出しOracle Objects for OLEの歴史は,Visual Basicの歴史だ.

Ver 1.x
 Oracle Objects for OLE以前に,オラクルから発売されていたミドルウェアは, Glueだ.このGlueとOracle Objects for OLEの一番の違いは,インターフェイスがAPIかCOMかの違いである.つまり,マイクロソフトが,APIインターフェイスのODBCからCOMインターフェイスのOLE DBに移行する何年も前に,すでにオラクルは同様の乗り換えを完了していたのだ.これはオラクルの先見性が証明されたと言えるだろう.
 Oracle Objects for OLEの歴史は,Visual Basic 3.0の時代から始まる.Visual Basic 3.0は,障害が多い2.0の替わりに投入された製品で,その障害の少なさを考慮すれば,Visual Basic 4.0が発売されたあとでも,16bit版Visual Basicの決定版だ.
Visual Basic 3.0をターゲットにしたOracle Objects for OLEももちろん16bit版だ.当時のプアなハードウェアおよび16bit版ということもあって,あまり性能のよいものではなかった.

Ver 2.0
 Ver 2.0は,Visual Basic 4.0の発売に合わせて登場したOracle Objects for OLEの最初の32bit版バージョンだ.
 Ver1.xとVer2.0の互換性は高く,16bitアプリのコードをそのまま32bitアプリに移植することができた.もっとも,Visual Basic 4.0の16bitと32bitの違いが大きく,現実的には,Oracle Objects for OLE以外の部分での修正が必要だった.

Ver 2.1
 Ver2.1は,発売当初Oracle Objects for OLE データコントロールが含まれていなかった.Personal Oracle7 R7.3に添付されているバージョンからデータコントロールが含まれている.Ver1.x時代からアナウンスされつづけていた機能がやっと実装されたことになる.もっとも,所詮はデータコントロールだから,細かな制御を必要とするアプリケーションで使う局面は,ほとんどない.しかし,機能があって使わないのは,機能がなくて使えないのとは異なり,判断を開発者に委ねられている分だけ気分がよい.また,マスタメンテナンスなどのように,必須だけれど予算または期間などを確保しづらい機能を実装するときに使うことが考えられる.

Ver2.2
 Ver2.2は,Oracle Objects for OLE本体の機能追加が目立つバージョンだ.ASPに対応してマルチスレッド対応になったり,タイプライブラリが正式にサポートされた.また,オブジェクトの参照スピードが向上しているなど,性能UPも見逃せない.
 ただ,V2.2.2まではNet8に対応していないなど,Oracle7からOrace8の切り替え時期と重なってしまい,バージョン番号による機能識別がわかりづらくなっている.
 なお,タイプライブラリは,Ver2.1などにも付属していたが,非公開機能であった.そのため,Ver2.1以前のタイプライブラリとV2,2以降のタイプライブラリには互換性がない.もし,Ver2.1以前のタイプライブラリを参照設定して作成したEXEが稼動している環境のOracle Objects for OLEをVer2.2以降に更新すると,今まで動作していたアプリケーションがエラーになることがあるので注意してほしい.もちろん,タイプライブラリを参照設定していないアプリケーションについては,バージョンを上げても正常に動作する.

Oracle Objects for OLE 2.3の新機能

 Oracle Objects for OLEの最新版は,原稿執筆時点では,V2.3だ.V2.3のオブジェクト,メソッド,プロパティを記事末表1にまとめた.

コネクションプーリング
 V2.3から追加されたコネクションプーリングの機能は,実際にコネクションが使われる前に,ある程度の個数のコネクションを張っておくことにより,データベースへの接続/切断に伴うオーバーヘッドを軽減することができる.Visual Basicで使うとすれば,図2のようにActiveX EXEからコネクションを張るように作成して,業務アプリからは,COM経由で接続するというような使い方になる.ActiveX EXEのスレッド数とコネクションプーリング数を同一値にすることで,接続動作を行なわずにRDBMS上のデータを操作できる.ただし,COMのオーバーヘッドを考慮すると,あまり効果的な構成ではない.EXEを別にしたいけど,いちいちログオン操作をするのは面倒だということであれば,起動時パラメータに接続情報を付加して,プログラム起動時に自動接続するのがよい.
 しかし不思議なことに,ログオン操作だけではなく,ログオン処理時間を短縮するために,このような構成を利用したため,逆にCOMのオーバーヘッドに苦しんでいるという開発プロジェクトもあり,そのようなプロジェクトでは,今まで自前でコーディングしていたコネクションプーリング機能が提供されたことは,多少の朗報になるだろう.
 実際,このコネクションプーリングが活躍するとすれば,接続と切断が繰り返されるようなときだ.それは,たとえばASP2.0で使うときだ.ASP2.0では,global.asaを使うことにより,ASP仮想ディレクトリに対応したアプリケーション内での永続的オブジェクト(ASPアプリケーションが最初に使われたときから,IISが終了するまで有効なオブジェクト)を定義することができる.OraSessionオブジェクトをこの永続的オブジェクトとして定義し,その上でコネクションプーリング機能を使うことで,断続的に接続と切断が繰り返されるであろうWebアプリケーションのパフォーマンスを向上することができる.

図2:オーバーヘッド軽減のために
図2:オーバーヘッド軽減のために

複数のPL/SQLカーソルの取得
 V2.3以前でも,CreatePLSQLDynasetメソッドにより,PL/SQLで定義したカーソルを使ってレコードを取得することができた.なぜ,このような機能があるかといえば,この機能を使うことでカーソルの定義をRDBMSで一元管理することができるからだ.WHERE句の条件を変更するのに,VBの開発環境を起動して,修正→EXEの作成→配布を行なわなくてもよいわけだ.
 V2.3では,そのPL/SQLカーソルの機能を拡張して,1度に複数のカーソルを作成できるようにした.
 事前にパラメータとしてカーソルを戻すストアドプロシージャを用意して,それを指定して
Set objSQL= objDb.CreateSql( _
 strSQL,ORASQL_FAILEXEC
のようにOraSQLStmtオブジェクトを定義(図3(1))して,同じOraDatabaseオブジェクトにOraParameterオブジェクトを生成させる(図3(2)).そして,そのOraParameterオブジェクトにカーソル変数を指定して,
Set objDs = _
 objDb.Parameters("curOrder").value
のようにOraDynasetオブジェクトを生成する(図3(3)).
 こうして作成したOraDynasetオブジェクトは,もちろんCreateDynasetメソッドにより生成したOraDynasetオブジェクトと同じ操作が可能だ.

図3:OraDynasetオブジェクトの生成
図3:OraDynasetオブジェクトの生成

Oracle Objects for OLEの基礎知識

小見出しログインするには

 Oracle Objects for OLEでOracle7/8にログインするには,その最下層のプロトコルレベルから接続を確認する必要がある.

TCP/IPレベルの確認
クライアントマシンのDOSプロンプトからサーバマシンの名前(たとえば,ora8.vbmag.shoeisha.co.jp)を指定し,
ping ora8.vbmag.shoeisha.co.jp
と入力して,Replyが返ってくれば,TCP/IPレベルの確認は完了だ.もし,Replyが返ってこなければ,クライアント,サーバ双方のネットワーク定義やLANなどの通信周りを確認する.

通信ミドルウェアレベルの確認
 Oracle7/8用の通信ミドルウェアとしては,SQL*NetまたはNet8がある.それぞれ,SQL*Net Easy ConfigまたはNet8 Easy Configを使って,データベース別名(たとえば,vbmag)を定義して使用する.定義するときには,先ほどのpingで指定した名前をサーバ名として指定することになる.
定義が完了したら,クライアントマシンのDOSプロンプトから
tnsping vbmag
 または
tnsping8 vbmag
と入力して正しい応答があるか確認する.
 応答がないときや正しい応答ではないときは,データベースインスタンスなどの定義をサーバ側で確認する.

Oracle Objects for OLEからの接続
 通信ミドルウェアレベルの確認に成功したら,Oracle Objects for OLEでの接続が可能だ.接続するにはリスト1のように,CreateObject文でアプリケーションとOracle Objects for OLEの接続を行ない,DbOpenDatabaseメソッドでOraDatabaseオブジェクトを作成して,データベースのログオンを行なう.

リスト1:Oracleへのログオン
Const mcOLE = "OracleInProcServer.XOraSession"

Private Sub cmdOK_Click()
  Dim strConnect      As String
  Dim strHost         As String

  strConnect = Trim$(txtUser.Text)
  If Len(Trim$(txtPass.Text)) > 0 Then
    strConnect = strConnect & "/" & Trim$(txtPass.Text)
  End If
  strHost = Trim$(txtHost.Text)

  Set pobjSess = CreateObject(mcOLE)
  Set pobjDb = _
   pobjSess.DbCreateDatabase(strHost, strConnect, 3&)
End Sub

小見出しデータを取得するには

 Oracle7/8からデータを取得するには,OraDatabaseオブジェクトのDbCreateDyansetメソッドを使う(リスト2).

リスト2:データの取得
Private Sub cmdSearch_Click()
  Dim strSQL        As String
  Dim objDs         As Object
  Dim strErrText    As String

  On Error GoTo errClick:

  strSQL = "SELECT * FROM emp"
  Set objDs = pobjDb.DbCreateDynaset(strSQL, 8&)	…(1)
  lstResult.Clear
  Do While Not objDs.EOF				…(2)
    lstResult.AddItem objDs("empno").Value		…(3)
    objDs.DbMoveNext					…(4)
  Loop

exitClick:
  On Error Resume Next
  Set objDs = Nothing					…(5)
  Exit Sub

errClick:
  If pobjSess.LastServerErr = 0 Then
    If pobjDb.LastServerErr = 0 Then
      strErrText = Error$
    Else
      strErrText = pobjDb.LastServerErrText
      pobjDb.LastServerErrReset
    End If
  Else
    strErrText = pobjSess.LastServerErrText
    pobjSess.LastServerErrReset
  End If
  MsgBox strErrText, vbOKOnly + vbExclamation, App.Title
  Resume exitClick:
End Sub
(1) ログオンが成功したpobjDb変数に対してCreateDynasetメソッドを実行する.このメソッドの第一パラメタはSELECT文,第二パラメタはオプションになる.
(2) CreateDynasetメソッドが成功すると,objDs変数ととカーソル情報(Oracle Objects for OLEの世界では,OraDynasetオブジェクトという)が関連付けられます.よって,検索した結果をすべて取得するときには,EofになるまでobjDs変数が参照している情報を取得する.もし,1レコードも検索できなかった場合は,CreateDynasetメソッド実行直後で既にEofがTrueになる.
(3) objDs変数が示すレコードの情報を取得する.
(4) OraDynasetオブジェクトのMoveNextメソッドを使って次のレコードを取得する.Eofまでのループのなかで,MoveNextメソッドを忘れると無限ループに陥るので注意.
(5) 使いおわったOraDynasetオブジェクトは,Nothingを使い明示的にCloseする.

小見出しデータを更新するには

 Oracle7/8のデータを更新するためには,OraDynasetオブジェクトのメソッドを使う(リスト3).

リスト3:データの更新
Private Sub cmdUpdate_Click:

  On Error Goto errClick:

  strSQL = "SELECT * FROM emp"
  Set objDs = pobjDb.DbCreateDynaset(strSQL, 0&)
  If objDs.Eof Then
     objDs.DbAddNew							…(1)
  Else
      objDs.DbEdit							…(2)
  End If
  objDs("ename").Value = "TestUsers" & objDs("ename").Value		…(3)
  objDs.Update								…(4)

exitClick:
  On Error Resume Next
  Exit Sub

errClick:
  If pobjSess.LastServerErr = 0 Then
    If pobjDb.LastServerErr = 0 Then
      strErrText = Error$
    Else
      strErrText = pobjDb.LastServerErrText
      pobjDb.LastServerErrReset
    End If
  Else
    strErrText = pobjSess.LastServerErrText
    pobjSess.LastServerErrReset
  End If
  MsgBox strErrText, vbOKOnly + vbExclamation, App.Title
  Resume exitClick:
End Sub
(1) DbAddNewメソッドにより挿入モード
(2) DbEditメソッドにより更新モード
(3) 列に値を設定(実際は,クライアント側のバッファに格納).
(4) Updateメソッドを実行して,クライアント側のバッファからデータベースのレコードに値を反映

 もちろん,UPDATE SQL文指定してExecuteSQLメソッドを実行することで,データを更新することもできる.

小見出し型の変換

 Oracle Objects for OLEは,Oracle7/8のフィールド型とVisual Basicの変数型の相互変換機能を備えている(表2).

表2:データ型一覧
Oracleのデータ型 Visual Basicのデータ型
CHAR String
DATE Variant
LONG String
LONG RAW String
NUMBER (1-4,0) Integer
NUMBER (5-9,0) Long
NUMBER (10-15,0) Double
NUMBER (16-38,0) String
NUMBER (1-15,n) Double
NUMBER (16-38,n) String
RAW String
VARCHAR2 String

小見出しバイナリデータの扱い

 バイナリデータの扱いは,Oracle Objects for OLEの数少ない鬼門の一つだ.そもそも,RDBMSにバイナリデータを含むこと自体に無理があるとも言える.AppendChunkByteなどのバイナリデータを扱う機能は存在するが,RDBMSにはバイナリデータの管理情報のみを置き,そこに格納されたサーバー名,ディレクトリ名,ファイル名を使って,FTPや共有フォルダ経由でバイナリファイルを送受信するのが良いだろう.

小見出しバインド変数

 Oracle7/8は,SQL文を解釈するときに,解釈済みのSQL文と文字列として比較してまったく同一のときは,構文チェックおよび実行計画の作成などの工程をスキップして,SQL文の実行を開始する.この解釈済みのSQL文は,SGAと呼ばれるメモリ領域上に保存される.そして,このSGAは,ユーザーごとではなくRDBMS全体で管理されているので,そのRDBMSを使うシステム同士でSQL文のコーディング規約を決めることにより効率良くSQL文の再利用が可能になる.
 しかし,現実的な話に目を向けると,あるテーブルに対してWHERE句の条件値をいろいろ変化させてその結果を表示して,人が何かを判断するような使い方が多いと思う.そのようなときに,条件値を指定する変わりに,

SELECT *
  FROM emp
WHERE empno= :emp
のようにバインド変数を指定して,同一のSQL文を仕立て上げる.

リスト4:バインド変数
  pobjDb.Parameters.Add "strEname", "0", 1		…(1)
  strSQL = "UPDATE emp SET ename = ename || strEname"
  For iintLoop = 1 To 1000
    pobjDb.Parameters("intID") = iintLoop
    lngRet = pobjDb.DbExecuteSQl(strSQL)
  Next

exitClick:
  On Error Resume Next
  pobjDb.Parameters.Remove "strEname"			…(2)
  Exit Sub

errClick:
  <エラー処理>
  Resume exitClick:
(1) バインド変数を定義
(2) バインド変数を削除

 バインド変数を使うときの注意点は,バインド変数の型とOracle側での型を一致させることだ.OraParametersオブジェクトのAddメソッドの第二パラメータは,バインド変数の初期値であると同時に変数型の決定にも影響する.型の設定はこの方法でも良いのが,プログラムの可読性を上げるためには,oraParameterオブジェクトのServerTypeプロパティを使って,明示的に型を指定した方がよいだろう.

小見出しバインド配列

 V$SQLAREAビューを検索したときのように大量のデータをクライアント側に表示したいときに,レスポンスが問題になることがある.時にLANが混雑していたり,ISDNなどの低速回線を使ってWAN接続しているときなどは,ダイナセットで取得したレコード件数に表示レスポンスが大きく左右されると思われる.その解決方法の一つがPL/SQL表(oo4oではバインド配列と呼ぶ)を使う方法である.

リスト5:バインド配列
  poraDb.Parameters.Add "intCnt", 0, 2
  poraDb.Parameters.AddTable "astrItem", 2, 1, 1000, 20			…(1)
  Set objSpread = poraDb.Parameters("astrItem")				…(2)
  strSQL = "BEGIN pkgTEST.subTEST1(:astrItem,:intCnt); END;"		…(3)
  poraDb.DbExecuteSQL (strSQL)						…(4)

  grdResult.Rows = poraDb.Parameters("intCnt").Value \ 8		…(5)
  grdResult.Row = 0
  For ilngRow = 1 To grdResult.Rows - 1
    grdResult.Row = grdResult.Row + 1
    For ilngCol = 0 To grdResult.Cols - 1
      grdResult.Col = ilngCol
      lngPos = ilngCol + (ilngRow - 1) * 8 + 1				…(6)
      grdResult.Text = poraDb.Parameters("astrItem").get_Value(lngPos)	…(7)
    Next
  Next

exitClick:
  On Error Resume Next
  poraDb.Parameters.Remove "intCnt"
  poraDb.Parameters.Remove "astrItem"					…(8)
  Set objSpread = Nothing						…(9)
  Exit Sub
(1) AddTableメソッドにより,バインド配列「astrItem」をVARCHAR2型の出力パラメタとして定義
(2) バインド配列をオブジェクト変数に設定
(3) ストアドパッケージを実行するSQL文を設定
(4) SQL文を実行
(5) バインド配列の大きさを取得
(6) バインド配列の要素を取り出す位置を計算
(7) バインド配列の値を取得
(8) 使いおわったバインド配列はRemoveメソッドで破棄
(9) 使いおわったバインド配列用オブジェクト変数はNothingで破棄

 PL/SQL表でどのくらいのサイズの配列が受け渡せるかは,Oracle Objects for OLE側の制限が上限となる.ServerTypeごとに設定可能なArraySize(配列の要素数)の上限があり,またServerTypeをVARCHAR2などの文字列長にしたときにはElementSize×Arraysizeが32KB以内だ.

小見出し日本語項目名の扱い

 Oracle Objects for OLEは,日本語のフィールド名およびテーブル名などを扱うことができる.しかし,フィールド名およびテーブル名を始めとするRDBMSの物理定義に日本語を使うことは,お勧めできない.現に,日本語の列名をSELECT句に指定するときは,“,(カンマ)”の後ろに半角空白を入れないとエラーになる.

小見出しアプリケーションの配布

 Oracle Objects for OLEは,ランタイムライセンスフリーの製品だ.つまり,開発環境分のライセンスさえ購入すれば,あとは好きなだけ無料で配布することができる(Oracleと接続するのに必要なSQL*NetまたはNet8は,すべての稼動環境にライセンスが必要).よって,配布時の注意点は,Visual Basic製品ではないので,自動的にディストリビューションウィザードで検出されないということくらいだ.
 Oracle Objects for OLEを動作させるのに必要なファイルは,

OIP23.DLL
ORAIPSRV.REG (ORAIP_ST.REG)
OIP23.TLB
ORAANSI.DLL
だ.Oracle Objects for OLEを使っているときは,これらをアプリケーションの一部として配布して,<ORAHOME>\BINディレクトリにインストールする必要がある.また,ORAIPSRV.REGにある情報をレジストリに登録する.

regedit.exe oraipsrv.reg
 さらに,データコントロールを使用するアプリケーションは,次のファイルの配布が必要だ.
ORADC.OCX
 データコントロールを使用する場合は,ファイルをアプリケーションに含めるだけでなく,
regsvr32.exe oradc.ocx
として,同様にWindowsのレジストリに登録する.

最後に

 もし,COMが扱えない言語で開発せざるを得ないとすれば,OCI(Oracle Common Interface)と呼ばれるAPIを使わなければならない.OCIは,SQL ServerにおけるDB-Libraryのようなものなので,APIを直接使うという技術者心をくすぐられる行為ではある.実際にやりたい事が「OCIを使う」という行為そのものではなく,「Oracleと接続したい」ならば,特にOCIを意識する必要はないだろう.

表1:Oracle Objects for OLEのオブジェクト,メソッド,プロパティ


VB Magazine ライブラリ | Visual Basic WorkGroup
int21 ホームページ | PCDN ホームページ


Copyright (c) 1998 int21 Corporation All Rights Reserved.
For questions or comments, please send mail to: pcdn@int21.co.jp