初音 玲 HATSUNE, Akira
MTSのトランザクション機能の実態とは
Microsoft Transaction Server(MTS)は、トランザクションを実際に管理する部分を保有していない。MTSは、MTSオブジェクトからメソッドを呼び出し、ODBCドライバがRDBMS対して行なっているトランザクション処理を、MTS経由で別の方法で行ない、さらにMTS自身もそのトランザクションを覚えておく(ステートフルという)というものだ(図1)。
SQL Serverの場合は、ODBCとMS DTCを使ってトランザクション機能を実現する。Oracleの場合は、上記の2つにプラスしてXAインターフェイスも使うことになる。
MTSトランザクション機能を使う局面
MTSに登録したMTSオブジェクト(その実態は、MTSのメソッドを呼び出しているActiveX DLL)で、複数のメソッド間にまたがったトランザクションをサポートしたいときには、MTSのトランザクション機能を使うと便利だろう。MTSオブジェクトを呼び出すベースクライアントは、トランザクションではなく、順番だけを気にしていればよく、トランザクションの状態をクライアント側で意識することはない(図2)。これは、Visual Basicなどでベースオブジェクトを作ったときにロジックが簡単になるだけではなく、IISをベースクライアントとする(ASPからMTSオブジェクトを使う)ことにより、Webページ間の情報の受け渡しを、cookieなどを使わずに実現できるということだ。
MTSトランザクション機能をOracle8に適用する
前号の特集でも触れたが、MTSが対応しているOracleのバージョンは、Oracle7だ。そのため、MTSを実行するマシンには、SQL*Net Client 2.3.4以上が必要となり、MTSとOracle8を同一マシン上で稼動することはできない。1台のNT上にサービスはひとつという大前提を踏まえれば、このような制限は大した事ではない。しかし、ものは試しに、定義を無理やり変更して、Net8 Clientを使って、MTSからXAインターフェイスを経由できるかを探ってみる。
Oracle8クライアントに適用するには?
MTSからOracle8への通信には、MS DTCとXAインターフェイスを用いる。これは、MTSからはMS DTCに見えて、Oracle8からはXAインターフェイスにみえるということだ。MTSでSetComplateメソッドを発行して、MTSトランザクションの終了を通知すると、XAインターフェイスを経由してOracle8に指示が出る。つまり、SQL*Net Client 2.3.4ではなく、Net8 Client用のXAインターフェイスを呼び出せばよい。
1.SYSユーザでOracleにログオンする 2.V$XATRANS$ビューを作成する (ORA_HOME \RDBMS80\ADMINに あるxaview.sqlを投入する) 3.SELECT権限を与える GRANT SELECT ON V$XATRANS$ TO PUBLIC 4.MTS(Oracleクライアント)のレジトリ HKEY_LOCAL_MACHINE \SOFTWARE \Microsoft \Transaction Server \Local Computer \My Computer の下にある次の2つのキー値を変更する。 OracleSqlLib sqllib18.dll → sqllib80.dll OracleXaLib Xa73.dll → Xa80.dll
この操作により、MTSエクスプローラは、一見正常動作しているように見える。しかし、実のところトランザクション処理が何も行なわれない状態になっている。また、TestOracleXaConfigを実行してみると
Calling xa_open_entry
のプロセスで-3のリターンコードが返却される。実行途中のメッセージが固定化されているので、レジストリの内容を無視しているためとも思えるが、サンプルプログラムのトランザクションが無視される事実からも、まともに動作させることができないということだろう。
やはり、Oracle7クライアントが必要だ
そこで、レジストリの内容を元のSQL*Netのバージョンに戻して、SQL*Net EasyConfigで作成したデータベース別名を指定し、TestOracleXaConfigを実行すると、またしてもMTSエクスプローラは正常に動作しているように見えるのだが、今度は、
Calling oopen
のプロセスで、ORA805.DLL内でページ違反が発生して異常終了する。つまり、勝手にOracle8のクライアントモジュールが使われてしまうのだ。そこで、Oracle8のクライアントモジュールをアンインストールすることになるのだが、簡単にはいかなかった。まず、MTSのMTSエクスプローラから「DTC停止」および「サーバコンポーネントのシャットダウン」をする必要がある。そうしないとOracleのクライアントモジュールの一部が使用中のために削除できないので注意して欲しい。そして、Oacle8のクライアントモジュール(Net8 ClientとRSF8.x)およびMTSを削除する。この操作を行なえば、MTSエクスプローラの動作もTestOracleXaConfigの結果も正常になる (リスト1) 。もし、正常に動作しないようであれば、MTSのアンインストールと再インストールをするとよい。
なお、NT4.0のSP4により更新されるMTS2.0(SP1)のヘルプファイルを見るとOracle7 R7.3.4以外にOracle8もサポートしているように思えるが、Oracle7クライアントモジュール経由で接続することには変わりがない。
リスト1:testOracleXaConfig実行結果
testOracleXaConfig -Usystem -Pmanager -Skawa7 ****** MTS/Oracle COnfiguration Test Utility ****** Loading XA73.DLL...succeeded Loading SQLLIB18.DLL...succeeded Loading OCIW32.DLL...succeeded Getting the xa_switch switch...succeeded Getting the sqlld2 function...succeeded Getting the opinit function...succeeded Getting the oopen function...succeeded Getting the xa_open_entry...succeeded Getting the xa_start_entry...succeeded Getting the opinit...succeeded Getting the sqlld2...succeeded Getting the oopen...succeeded Test successfully completed!
そしてトランザクションを利用する
ODBCを使うMTSオブジェクトを作成するのならば、この段階で環境は整ったことになる。サンプルプログラムODBCMTS(リスト2)で動作を確認してみる。ODBCMTS.DLLをMTSに登録するには、MTSエクスプローラを起動して、手順2のようにする。
![]()
- [マイコンピュータ]→[インストールされたパッケージ]を選択
- [ファイル]-[新規作成]メニューから[パッケージウィザード]を起動
- 初めて登録するので、[空のパッケージの作成]を選択し、パッケージの名前を入力
- [インストールされたパッケージ]→[FirstMTS]→[コンポーネント]を選択
- [ファイル]−[新規作成]メニューから[コンポーネントウィザード]を起動
- [新しいコンポーネントをインストールする]を選択
- [コンポーネントのインストール]を起動
- ActiveX DLLのファイル名を追加
Scottユーザでログインし、[INSERT]ボタンを押して(最初に1度クリックするだけでよい。2度目以降はレコードの重複エラーになる)テストデータを挿入したら、[UPDATE]ボタンによる更新成功と[ERROR]ボタンによる更新失敗の情報をSQL*Plusなどから、
SELECT * FROM EMP
と入力することでレコードの内容を参照し確認して欲しい。
また、MTSのトランザクション自体は、MTSエクスプローラの[トランザクションの統計]画面(図3)に記録されるので、レコードの内容と共に確認するとトランザクションのようすがわかるだろう。
図3:トランザクションの統計
![]()
やはり、oo4oからは使えない
Oracle Objects for OLEは、データベース別名でSQL*Net ClientとNet8クライアントのどちらを使うかを判断する。そのため、Oracle8製品に含まれているOracle Objects for OLEは、バージョンによっては、Net8クライアントのモジュールが必要なときがある。こうなってくると八方塞がりだ。さて、SQL*Net Client 2.3.4で稼動するバージョンを入手できたとして、やはり、Oracle Objects for OLEからは利用できないようなのだ。ODBCMTSサンプルが正常に稼動する状況で、oo4oMTSサンプルを稼動させて、同様の操作をしてもMTSトランザクションは機能しない。
結局は
結局は、MTSおよびMS DTCが対応しているOracleのクライアントモジュールがOracle7止まりであり、Oracle8製品に含まれるクライアントモジュールがOracle8クライアントモジュールの使用を前提にしてるために、かなり煩雑な状況になっているということだ。Oracle8クライアントモジュールからOracle7への接続をサポートしていることを考えると、マイクロソフトには、ぜひMS DTCからもOracle8クライアントモジュールの利用が可能なよう対応して欲しいと思う。
MTSオブジェクトの更新
・バイナリ互換を設定しておくこと ・DLLに再コンパイルするときは、[DTCの停止]と [サーバーコンポーネントのシャットダウン]を MTSエクスプローラから行なっておくこと ・サーバーコンポーネントのシャットダウンは、 [CTRL]+[ALT]+[DEL]で表示されるウィンドウ (Windows 95/98)またはタスクリスト(Windows NT 4.0)の中に[Mtx]というタスクがなければ成功 している。ただし、タスクがなくなるまで、何秒か のタイムラグがある。 ・コンパイルが終わったら、MTSエクスプローラから [DTC開始]と[すべてのコンポーネントの更新]を 行なう
MTSオブジェクトプール機能をOracle8に適用する
MTSトランザクションに頼らずに、Oracle Objects for OLEのトランザクションに頼れば、MTS上でOracle Objects for OLEアプリケーションを期待したように動作させることができる。そうして、このようにMTSオブジェクトプール機能を使うだけなら、Oracleのクライアントモジュールの細かな違いなどに悩まずに済む。
サンプルoo4oMTS2(リスト3)にあるようにMTSトランザクション用のメソッドの変わりに、Oracle Objects for OLEのトランザクションメソッドを呼ぶだけでよい。
リスト3:oo4oMTS.DLL(抜粋)
Public Sub mtdEdit() On Error GoTo errEdit: moraSes.DbBeginTrans moraDs.DbEdit If moraDs("ENAME").Value = "HATSUNE" Then moraDs("ENAME").Value = "AKIRA" Else moraDs("ENAME").Value = "HATSUNE" End If exitEdit: On Error Resume Next Exit Sub errEdit: moraSes.DbRollBack App.LogEvent Error$ Err.Raise vbObjectError End Sub Public Sub mtdUpdate() On Error GoTo errUpdate: moraDs.DbUpdate Set moraDs = Nothing moraSes.DbCommitTrans exitUpdate: On Error Resume Next Exit Sub errUpdate: moraSes.DbRollBack App.LogEvent Error$ Err.Raise vbObjectError End Sub Public Sub mtdError() On Error GoTo errUpdate: moraDs.DbUpdate Set moraDs = Nothing moraSes.DbRollBack exitUpdate: On Error Resume Next Exit Sub errUpdate: App.LogEvent Error$ Err.Raise vbObjectError End Sub
最後に
なんだか、リベンジするつもりが、リベンジ返しにあってしまった気もする。MTSオブジェクトプール機能だけに限っていえば、何も悩まずにすんなりと動作してしまうのだが、MTSトランザクション機能については、たとえODBC経由(今回は、ODBC Directをミドルウェアに選定した)で使ったとしても、環境を整えるのに非常に苦労した。OSから入れなおしたり、MTSやOracleのクライアントモジュールも何度も再インストールした。さらに該当するバージョンを探し回ったりと、なんだか、それだけでエネルギーを使い果たすほどであった。
そういえば、ASPの話を書いたときも同じような苦労をしたような気がする。まあ、あれは結果的に実りのあるものだったので、気持ち的には非常に良かったのだが、今回は最悪だ。前号の特集の冒頭で述べたように、"面倒な気がする"という予感だけが的中したのであろう。
まあ、これでしばらくはMTSに近づくこともないだろう。新バージョンがでて、何か状況が変わるようだったら、また取り上げてはみたいが、果たしてOracleに対して使い物になる日はくるのだろうか。
レジストリ情報の削除
ActiveX DLL Regsvr32 /u ActiveX DLLファイル名 ActiveX EXE パラメタ /unregserverつきで起動 [稼動確認環境] Windows 95 4.00.950c IE 4.0 4.72.3110.8 MTS 2.00.00.546 Visual Basic 6.0 (SP2) Oracle Objects for OLE 2.2.3 SQL*Net Client 2.3.4 Windows NT 4.0 Server (SP4) Oracle8 EE R8.0.5
リスト2:ODBCMTS.DLL(抜粋)
Option Explicit ' (1)Microsoft Transaction Server Type Libraryの参照設定 ' (2)MTSTransactionModeの設定 ' (3)ObjectControlインターフェイスの実装 Implements ObjectControl Private mobjCnt As ObjectContext Private mdbsODBC As Database ' RDBMSとのセション Private mrdynODBC As Recordset ' SELECT集合 Public Sub mtdSelect(rstrSQL As String) On Error GoTo errSelect: rstrSQL = "SELECT * FROM EMP WHERE EMPNO=7935" Set mrdynODBC = mdbsODBC.OpenRecordset(rstrSQL, _ dbOpenDynaset, 0, dbPessimistic) exitSelect: On Error Resume Next Exit Sub errSelect: App.LogEvent Error$ Err.Raise vbObjectError End Sub Public Sub mtdEdit() On Error GoTo errEdit: mrdynODBC.Edit If mrdynODBC("ENAME").Value = "HATSUNE" Then mrdynODBC("ENAME").Value = "AKIRA" Else mrdynODBC("ENAME").Value = "HATSUNE" End If exitEdit: On Error Resume Next Exit Sub errEdit: App.LogEvent Error$ Err.Raise vbObjectError End Sub Public Sub mtdUpdate() On Error GoTo errUpdate: mrdynODBC.Update mrdynODBC.Close mobjCnt.SetComplete exitUpdate: On Error Resume Next Exit Sub errUpdate: App.LogEvent Error$ Err.Raise vbObjectError End Sub Public Sub mtdError() On Error GoTo errUpdate: mrdynODBC.Update mrdynODBC.Close mobjCnt.SetAbort exitUpdate: On Error Resume Next Exit Sub errUpdate: App.LogEvent Error$ Err.Raise vbObjectError End Sub Public Sub mtdLogon(rstrHost As String, rstrConn As String) ' ログインIDやパスワード、データベース別名を必要に応じて ' 書き換えてください Dim strDSN As String Dim strConnect As String On Error GoTo errLogon: DBEngine.DefaultType = dbUseODBC strDSN = rstrHost strConnect = rstrConn Set mdbsODBC = OpenDatabase(strDSN, dbDriverNoPrompt, _ False, strConnect) exitLogon: On Error Resume Next Exit Sub errLogon: App.LogEvent Error$ Err.Raise vbObjectError End Sub Private Sub ObjectControl_Activate() ' CanBePooledが呼び出された後、MTSオブジェクトが ' 提供されるときにMTSから呼び出される。 On Error GoTo errActivate: Set mobjCnt = GetObjectContext mobjCnt.DisableCommit exitActivate: On Error Resume Next Exit Sub errActivate: App.LogEvent Error$ Err.Raise vbObjectError End Sub Private Function ObjectControl_CanBePooled() As Boolean ' MTSオブジェクトがインスタンス化されるときに、 ' Class_Initializeの次にMTSより呼び出される。 ObjectControl_CanBePooled = False End Function Private Sub ObjectControl_Deactivate() ' クライアントからMTSオブジェクトを使い終わったときに、 ' MTSより呼び出される ' この後、再利用されるときは、Activateメソッドが呼び出される ' 破棄されるときは、Class_Terminateが呼び出される On Error Resume Next mdbsODBC.Close Set mobjCnt = Nothing End Subサンプルプログラムのダウンロード --- mts02.lzh(49KB)
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