MySQL on Windowsと接続する
従来、System iのデータベース・アプリケーションを作成するのは非常に簡単でした。つまり、物理ファイルと論理ファイルを定義し、RPGのF仕様をコーディングすればデータベースを使い始めることができたからです。System iアプリケーションのほとんどはまだこのパターンに沿って作られていますが、System iアプリケーションを他のデータベースと連携させたいというニーズは以前から増えつづけています。他のタイプのサーバー上のデータベースにアクセスする必要がある場合は、自分で作ったものであれサード・パーティー製のツールであれ、何らかのタイプのコネクターを使用しなければなりません。
System i上で簡単に利用できるコネクターの中で最も有名なのがJavaのJDBCで、SQLデータベースへアクセスするのに必要な使いやすいクラス群を提供しています。JDBCを使用するには、連携しようとしているデータベース用のJDBCドライバーをインストールし、データベース・サーバーとSystem i側で必要な設定をし、JDBCドライバーと連携するJavaプログラムを用意する必要があります。
プラットフォームの異なるデータベースと連携するためのアクセス・テクニックの選択肢として、System iアプリケーションの開発者たちの間ではJDBCの人気が高まってきています。
本稿では、Windows上で稼働しているMySQLデータベースへSystem i上のアプリケーションからアクセスするJava JDBCサンプル・プログラムをどのように開発してテストしたらよいかについて説明します。また同時に、その過程において遭遇するであろうMySQL、JDBC、Javaのそれぞれの特徴についても説明します。
MySQLのインストールとテスト
Windows PCにMySQLをインストールするのは簡単です。ブラウザでdev.mysql.com/downloadsへ行って連携したいデータベースのフリー版をダウンロードします。MySQLをとりあえず使ってみたいという場合はCommunity Serverの最新の安定したリリースをダウンロードすると良いでしょう。私はMySQL 5.0をダウンロードしました。そして、System iからMySQLへアクセスするのにJDBCを使用するつもりだったのでMySQL Connector/J 5.0もダウンロードしました。ダウンロードしたらMySQLのインストール手順に従ってPC上にWorldのサンプル・データベースをインストールします。すべてをセットアップして使用できるようになるまでに約1時間程度かかります。
MySQL Query Browserを起動し、図1にある簡単なクエリーを実行します。このクエリーは1つのテーブルに対して発行する基本的なSQL SELECT文です。この時点では、Java/JDBCアプリケーションに出力させたいと思っているデータをMySQLテーブル中で確認するだけです。データベースを新規に作成するときは、この例のデータベースの中から始めると良いでしょう。
Java/JDBCアプリケーションの作成
MySQLとJDBCドライバー、およびサンプル・データベースをインストールし終えたら、Javaプログラムを開発する準備が整ったことになります。Javaプログラム開発の目的は、Query Browserで実行したSQL文がアクセスしていたのと同じデータベースに接続して同じSQL文を実行することです。同じ出力が得られればそのデータベースと連携する本物のアプリケーションを開発することに一歩近づくことになります。
図2にJdbcTest1というjavaのクラスを示します。Javaをご存知であれば、このクラスの一覧を見て、標準のJDBCのテクニックを使用してデータベースに接続し、SELECT文を実行し、その結果を表示していることがおわかりいただけるでしょう。Javaが初めてという方は、ほとんどのアクションがBセクションとDセクションで実行されていることがおわかりでしょう。これらのセクションではSQL文が実行されて結果セットからデータが抽出されて表示されています。
セクションAではClass.forNameメソッドを使用してMySQL JDBCドライバーを読み込んでいます。MySQL JDBCクラスの実際の名前はcom.mysql.jdbc.Driverで、Connector/Jからダウンロードしてインストールしたものです。Class.forName中で指定した名前を確実に探す方法で私が知っている唯一の方法は、JDBCをダウンロードした際に付随してくるドキュメントで調べるというものです。必要となる値はJDBCドライバーに関して最もよく質問のある2つの項目のうちの1つですから、ドキュメント中で値を探すのはそれほど難しくないはずです。JDBCドライバーに関してよくある質問のもう1つは接続文字列についてです。JDBCの接続文字列の使い方のテクニックについて調べたい場合も上記のドキュメントが唯一の拠り所でしょう。
セクションBのDriverManager.getConnectionメソッドにMySQL JDBC用の接続文字列の例が2つあります。1つ目のメソッドはコメント行になっています。MySQLの接続文字列について説明する前に、Class.forNameクラスの値と接続文字列が使用するJDBCドライバー固有なものであるということを理解しておかなければなりません。たとえば、Microsoft SQL Server用のJDBCドライバーを使用してMicrosoft SQL Serverに接続する必要がある場合、Class.forNameクラスの値と接続文字列がまた別のものになると考えてください。また別のデータベースを初めて使用することになった場合、そのデータベース用に示した一覧の中の似たような小さなJavaプログラムを再利用できます。そのJavaプログラムが動作するようになったら、例の中から値を自分のアプリケーションに容易にコピー&ペーストできます。
私が始めてJavaのテスト・プログラムを作成したとき、IBM WebSphere Development Studio Client (WDSc) for iSeries Advanced Edition version 7.0を使ってコーディングとテストを行ないました。つまり、MySQLデータベースが稼働しているのと同じPC上でJava/JDBCアプリケーションを開発してテストしたことになります。データベースがホスティングされているサーバーと同じサーバー上でJavaアプリケーションを実行するときは、図2のセクションBの最初の部分でコメント行となっている文(DriverManager.getConnection文)にあるような接続文字列を使用するのが普通です。この文は後述するJDBCのURLとlocalhostのホスト名、接続するデータベース名(World)を指定しています。MySQL JDBCドライバーがlocalhostをサーバーの名前として識別するJDBCのURLを処理する際、データベースへのアクセス権限がすでにあることを前提としています。
MySQLサーバーが実行されているマシンとは異なる別のマシンでJavaプログラムを実行するとき、認証を自動的にバイパスする手段はありません。つまり、図2の2番目のDriverManage文にあるように、接続URLの中にユーザーとパスワードを指定しなければなりません。このJavaプログラムをSystem i上で実行してPC上のMySQLデータベースにアクセスするというのが最終的な目的ですから、1番目のDriverManager文が動作するようになったらすぐに2番目のDriverManager文をテストしてください。2番目のDriverManager文をコーディングせずにJavaのコードをSystem iにエキスポートして実行させようとしても、MySQLサーバーへの接続に失敗します。接続を確立する際に重要なのは、まず利用できる簡単なテクニックを使用して接続できるようにすることです。Javaコードをデータベースが稼働しているPCと同じPC上で実行しているのであれば、ぜひともlocalhostオプションを使用してください。簡単な接続ができるようになったら、あるいはデータベースが稼働しているマシンと同じマシン上でJavaコードを実行できない場合は、接続文字列のネットワーク接続版に進んでください。
ネットワーク接続用の2番目のDriverManage文をコーディングしてテストする前に、MySQL Administratorプログラムのところに自分自身をMySQLユーザーとして追加してください。Schema Privilegesタブを使用してWorldデータベースに対するすべての特権を自分自身に付与してください。
接続文字列をコーディングする
ユーザー名とパスワードを設定したら、jdbc:mysql://WXP/World?user=cpelkie&password=cpelkieという接続文字列をコーディングします。この文字列はlocalhostの接続文字列よりも複雑です。この接続文字列の最初の部分はjdbc:mysql://となっており、接続文字列がMySQLの構成に関連するものであると認識できるようにするためのものです。他のJDBCドライバーを使用する場合は、接続文字列の最初の部分がそれぞれのドライバーに固有になっています。たとえば、System iのJDBCドライバーはjdbc:as400://を使い、Microsoft SQL Serverのドライバーはjdbc:mysql://となります。JDBC DriverManagerがJavaプログラムを正しいJDBCドライバーに接続できるように、接続文字列のこの部分に正しい値を記述する必要があります。
接続文字列の2番目の部分にはTCP/IPのホスト名かMySQLサーバーのアドレスを指定します。前出の接続文字列中のWXPという値は使用しているPCの名前です。このプログラムをSystem i上で動作するようにしたいのですから、System iがHostテーブルかDNSサーバーを使用してこのWXPという値を解決できるようにしておく必要があります。テスト用にWXPというホスト名とそのTCP/IPアドレスをSystem iのHostテーブル(CFGTCPメニューのオプション10)に追加してください。
ホスト名の後にはスラッシュ記号に続いてデータベース名を指定します。ネットワーク接続文字列では、データベース名をURLスタイルのパラメータで指定します。つまり文字「?」は名前と値パラメータのペアの始まりであることを意味し、文字「&」でパラメータを区切ります。ここでもJDBCドライバーのドキュメントでパラメータ名とその値を調べてください。たとえば、MySQLは「user」と「password」をパラメータ名として使用します。他のJDBCドライバーでは「uid」や「pwd」というパラメータ名が使われている場合もあります。あるJDBCドライバーで使用されている名前が他のJDBCドライバーでも使えると思い込まないでください。パラメータ名はJDBC標準の一部ではありません。各データベースのベンダーが自由にパラメータ名に対する値を決めています。
必要なパラメータだけを使用する
JDBCのドキュメントには過剰なほどのパラメータが記載されています。新しいデータベース・アプリケーションを初めて使おうというのであれば必要最小限のパラメータを使用してください。まずは1台のマシンから別のマシンへ確実に接続できるところへ接続文字列を持ってきてSQL文を実行することから始めてください。それがうまくいったらその他の何十個とあるパラメータのいくつかを調整して、それによってアプリケーションの動作がどう変わるのかを理解するのに時間を使っても良いでしょう。パラメータの多くは風変わりなもので、JDBCドライバーがどのように動作するかあるいはクエリーをどのように処理するかに影響します。データベースの開発元はユーザーが明示的に指定しないすべてのパラメータに対してデフォルト値を選択してあり、このデフォルト値であなたが作成した文をドライバーに実行させるのです。
ゆっくり進む
接続文字列を正しく設定するという作業は通常、JDBCアプリケーションを稼働させるために最も時間のかかる部分です。前述のコツコツと整然としたアプローチを採用するのであれば、自分のプログラムを一度目で動作させることができるでしょう。このテクニックは私が試したすべてのJDBCドライバーで動作します。一番重要な注意点は、ドライバーのドキュメントを入手することです。ドキュメントが手元にない状態でコーディングを始めようなどと決して思わないでください。正しいJDBC URLや接続文字列がどんなものなのかは想像する必要はありません。ベンダーがドキュメントの中にそれらの文字列について記載しています。
コーディングに戻って
さて、図2のセクションBのコードはSQLのSELECT文を作成して実行しています。セクションB (stmt.executeQuery method中の)のSELECT文を見てみると、図1のMySQL Query Browserで私がテストしたSELECT文と同じであることがわかるでしょう。また、テストのポイントはツールで実行したのと同じ結果がプログラムでも得られるか、ということです。もちろん任意のSQL文を実行できますが、コツコツと進んでいって同じ結果が出られるようにする方が実用的です。
セクションCはカラムのヘッダーを表示します。カラムのヘッダーはデータベース中の実際のカラムの名前と、コード中で増やしていく「Count」というカウンター値です。カラムのヘッダーは見栄えの良いものである必要はありません。選択したデータをレビューしたい場合は印刷してください。
セクションDはJDBCの結果セットを処理するループで、この結果セットはコード中ではrsとして参照されています。rs.nextメソッドは最初の行とその次の行を読み込みます。rs.getStringメソッドとrs.getIntメソッドは各行が順番にアクセスされるのに従ってカラム値を取り出します。取り出した値はsbという名前のStringBufferという型のオブジェクトに追加されます。オブジェクトsbはループを1回周るごとに作成され、またsbオブジェクトの値はループの終了時に印刷されます。セクションDは結果セットへの接続、文、接続をシャットダウンするためのcloseというメソッドで終了します。
セクションEはこのプログラムの中で最も重要なセクションです。このセクションではセクションBで始まったtryブロック中で発生するSQLExceptionのタイプのすべての例外をキャッチします。バグの結果生じる例外はおそらく複数個でしょうから、すべての例外(一般的にはそのうちの1つしかレポートされません)を取り出して表示するようなdoループを記述します。特定のSQLExceptionオブジェクトを使用するとエラーの詳細情報を取得することができます。SQLの状態やエラー・コードは特に役に立ちます。エラーの原因がエラー・メッセージからだけでは識別できないときは、ベンダーが提供するドキュメントで追加の情報を探すのが一般的だからです。
セクションFはその他のすべてのタイプの例外処理のために呼び出される汎用の例外ハンドラーです。通常のJavaプログラムではこのタイプの例外ハンドラーだけがみられます。汎用の例外ハンドラーは、ほとんどのアプリケーションのレポートがそうであるように、何かとてもひどいことが起こっているということを知らせるだけで、エラーの真相を知るには不十分な情報しか提供してくれないような不十分な一般的エラー・メッセージを処理するためのものです。特定のエラー処理機能を提供するJavaアプリケーションやその他のタイプのアプリケーションを作成するときは、セクションEにあるようなエラー処理機能を組み込んでおくのが常に最善の策です。私は時々自分が書いたJavaコードを見て「バカだなぁ」と思うことがあります。機能のためのコード以上にエラー・ハンドラーのコードの方が多いときがあるからです。しかし「絶対起きない」と思われるタイプのエラーに対処しておいて、それが発生してもその原因がすぐにわかれば、(いつも)眠気を誘われるようなエラー・ハンドラーすべてを含むようにしておいて良かったと思うことでしょう。
初期のテスト用に使うもう1つのテクニックはプログラムの終わりの部分(セクションF)で「終了」とシンプルに出力することです。プログラムの出力結果を図3に示します。プログラムの出力結果をQuery Browserの出力結果(図1)を比べてみるだけではなく、「終了」という一言のメッセージが表示されればプログラムが本当に終了したことがわかります。データを出力し、エラーがなかった旨のメッセージも出力しているということになれば、MySQLデータベースにアクセスするJava/JDBCプログラムが正しく動作していると自信を持っても良いでしょう。このプログラムをSystem i上に移植すれば、同じ出力結果が得られるでしょう。
移植前に考慮すること
System iに移植する前に、構成に関するいくつかの点について知っておかなければなりません。デフォルトではMySQLデータベースへのネットワーク接続はポート3306を使用します。デフォルトのポート番号を使用するときはJDBCの接続文字列でそれを指定する必要はありません。しかしTCPのポート番号を変更した場合は、JDBCの接続文字列も変更する必要があります。たとえば、ポート9906を使用するには接続文字列をjdbc:mysql://WXP:9906/World?user=cpelkie&password=cpelkieのように変更してください。
ポート番号に競合がない(あるいはその疑いがない)限りMySQLを稼働しているマシンのMySQLポート番号を変更しなければならない理由はありません。ポートを変更した場合は、変更したことを忘れずに、それに従ってすべてのJDBCの接続文字列を調整しなければなりません。使用するポートは通常のLAN環境では問題となりませんが、ファイヤー・ウォールを介して接続する場合はポートを新規に開く必要があるかもしれません。
移植する前にもう1つやっておかなければならないのはMySQLサーバーをシャットダウンし、作成したJava/JDBCプログラムを試動して、MySQLデータベースがダウンしているときにプログラムがどのように動作するか確認してみることです。MySQLをデフォルトのインストール環境で使用した場合、Windows PC上にサービスとしてインストールされます。Windowsサービスのダイアログを使用してMySQLサーバーのステータスを表示し、停止することができます(MySQLの項目を右クリックして停止オプションを選択します)。MySQLサーバーを停止したときはJava/JDBCプログラムを再起動してください。このときは徹底的なエラー・レポートが図4のように表示されます。このエラーに一度対処しておけば、また同じエラーが表示されたときにも十分に対処できるでしょう。
誤り箇所をテストしたら、MySQLのサービスを再起動します。System i上でプログラムを実行したときにサービスが起動している状態にしておくためです。
System i上でのJDBC
さてここまで来ればJavaのコードをSystem i上にエキスポートできます。System i上にMySQL JDBCドライバーをインストールしておく必要もあります。JavaアプリケーションをSystem i上に置くだけでは充分ではありません。そのプログラムを実行したときにJDBCドライバーがある場所を認知してドライバーを呼び出し、MySQLデータベースへのネットワーク接続が行なえるようになっていることを前提としています。
ただし、JavaコードとMySQL JDBCドライバーなどの製品をSystem iにインストールするときは、IFS中にインストールする必要があります。Javaのコードは通常のSystem iのライブラリ中にはインストールされません。多くのマシンでJavaが大きな問題となるのは、Javaアプリケーションが実行時にヘルパー・コード(たとえばJDBCドライバー)の位置がわかるようにJavaのクラスパスを設定する必要があるという点です。
幸いなことに、IBMは一般的に使用されるJavaコードをSystem i上に配置するための容易なテクニックを提供してくれています。JavaのクラスやJavaアーカイブ(JAR)ファイルをIFSディレクトリの/QIBM/UserData/Java400/extに配置した場合、System iのJava仮想マシン(JVM: Java Virtual Machine)はクラスやJARを探すときにクラスパス下を見る前に上記のディレクトリを見に行きます。これについてはIBM Developer Kit for Javaという参照マニュアルに記載されています。
上記のディレクトリにあるMySQL JDBC JARと他のJDBCドライバー用に使用するその他のJARファイルを図5に示します。ネットワーク・ドライブをこのIFSディレクトリにマップしてJARファイルをコピーするかまたはFTPで自分のPCに転送することができます。もし可能であれば、MySQL JDBC JARをこのディレクトリにインストールして初期のテストを実行する方がもっと簡単でしょう。
JARを上記のディレクトリにインストールしない方が良い(あるいはしてはいけない)理由は2つあります。1つ目は、このディレクトリにあるJARとクラスは他のクラスパス・ディレクトリにある他の似たような名前のJARやクラスが見つかる前に見つかるためセキュリティを公開してしまう可能性があるというものです。これはローグ・プログラムを典型的なライブラリ・リストの先頭近くの部分にインストールしたときに発生するのと基本的には同じ問題です。プログラマという人たちは本質的に他のユーザーよりシステムを「信用する」度合いが高いという点も考慮すべきでしょう。セキュリティの問題が発生した場合、インストールしたいコードが信用できる提供元から得たコードのオリジナルのバージョンであることを示すことができます。
コードを上記のディレクトリにインストールしないほうが良い2つ目の理由は、パフォーマンスに影響する可能性があるためです。JVMはこのディレクトリにあるJARやクラスをクラスパス中の他のディレクトリに移動する前にそのJARやクラスを調べなければならないからです。簡単な方法を選択してベンダーが提供したコードと自分が作成したコードを単にこのディレクトリに入れた場合、自分の作成したコードとベンダーのコードを別の場所に配置した場合に比べて、おそらくシステムのパフォーマンスが低下するでしょう。私の場合、システム上で発生したパフォーマンス問題の原因を究明できたことはありません。ベンダーが提供するJDBCドライバーなどといった再利用性の高いコードは共有のディレクトリに配置し、アプリケーション固有のコードはそれ専用のディレクトリに配置するというポリシーを採用することをお勧めします。JDBCドライバーとその他の共有コードをすべて1つのディレクトリに配置することの利点はコードを一箇所にまとめておけるということです。パフォーマンスの問題より厄介なのは、共有コードがシステム上で増殖しつづけ、共有コードの最新のパッチが適用されたバージョンがどこにあるのかだれもわからなくなってしまうということです。
エキスポートの際にすること
コードをWDScからエキスポートするのは簡単です。テスト・プログラムを1つのJARファイルにエキスポートする際に使用するExportウィザードを図6、図7、図8に示します。図8で、M:ドライブをエキスポート先と指定しています。前もって/MYSQLJDBCという名前のIFSディレクトリを作成して、Windowsのネットワーク・ドライブM:をこのディレクトリにマップしておきました。エキスポートのプロセスが終了したら、JdbcTest1.jarという名前のJARファイルがIFS中にできています。
Javaを実行する
真実の瞬間がやってきました。Javaアプリケーションのテストは終わりました。MySQLサーバーも準備できています。MySQL JDBC JARはIFSの共有ディレクトリにあります。作成したコードは専用のディレクトリのJARファイル中にあります。
プログラムを起動するためのRUNJVAコマンドのプロンプトを図9に示します。CLASSパラメータを入力します。このパラメータはパッケージ名とクラス名を完全に記述したものになります。私が使用したパッケージ名は図2のリストの最初の文にあります。クラス名はリスト中のクラス文に指定します。
CLASSPATHパラメータにはWebSphere StudioからエキスポートしてきたJARファイルの完全に正しい位置と名前を指定します。System iのJVMは起動すると/MYSQLJDBCディレクトリを見てJdbcTest1.jarファイルを見つけます。さらにこのファイルの中で、com.web400.mysql.jdbc.JdbcTest1クラスを探します。Class.forName文が見つかると(図2のセクションA)、JVMはIBMの共有ディレクトリを見てcom.mysql.jdbc.Driverクラスを含んでいるJARファイルを探し、それがこのディレクトリに置いたMySQL JARファイル中にあることを見つけます。JavaのShell Displayが表示され、以前に実施したテストの結果で見たものと同じデータが表示されます。
アプリケーションと拡張
これまでにJDBCを使用してSystem iから他のデータベースと連携する際に発生するさまざまな問題を見てきたと思います。朗報なのは、MySQLの問題さえ解決できればJDBCドライバーを入手できるその他のデータベースについても同じ問題を解決できたことになるということです。
JDBCドライバーとアプリケーションを構成し、テストし、インストールする方法に関する知識があれば、RPGとJDBCを連携させるテクニックを使用できるようになるまでもう少しです。System iアプリケーションから他のデータベースと連携できるようになることは必要とされるスキルであり、これらのテクニックを努力して理解しておけばあとになってその努力が報われるでしょう。