第5回 データベースのブラウザを作る
J2EEでのデータベースとの接続 |
説明の順序が逆になったのですが、先のJSPのコードでは前半部分が省略されています。Scriptletの最初の方に、connという変数が登場しているのですが、この変数は、Connectionという型を持っています。データベースで検索を実行するためには、まず、データベースとの接続が行われていなければなりません。Connection conn ; は、まさにデータベースとの接続のために必要なオブジェクトです。
ここでは、J2EEでのデータベースへの接続について、基本的なことを見ておきたいと思います。先ほど、実際には最も大事といっていいJDBCでのQueryの処理について、「定型的な処理です」と簡単に片付けておいて、あらためてデータベースの接続の話題を取り上げるのには、実は理由があります。JDBCの処理やデータベース接続については、JDBCのドキュメントに説明があるのですが、J2EEのもとでのデータベース接続については、ちょっとしたTipsが必要です。しかも、それは、分かりやすい形では文書化されていません。今回のテーブル・ブラウザを動かすためには、deploy時にその知識が必要なのです。
ドライバ・マネージャを利用した接続 |
最初に確認したいのは、J2EEは、JDBCを使ってデータベースと接続するのですが、それには2つの方法があるということです。
1つの方法は、次のようにドライバ・マネージャを利用する方法です。ここでは、まず、COM.cloudscape.core.RmiJdbcDriverというJDBCのドライバをロードして、ドライバ・マネージャに対してデータベースのURLを指定し、コネクションを獲得しています。このURLを見れば、データベースが、どこで(locahost)何番のポートで(1099)動いているのかが分かります。
try{ |
この方法は、直接的で分かりやすいのですが、次のような問題があります。こうしたコードを、プログラムの中に直接組み込んだとしましょう。そうすると、このプログラムは、データベースの稼働すベきホストやポートの情報がハード・コードされていることになりますので、開発者以外の人の環境で動かすことは難しくなります。開発者自身にとっても、マシンの環境が変わるのはよくあることですね。はっきりしているのは、こうしたコードを含んだプログラムは、商品にはできないということです。
データソースを使って接続する |
JDBC 2.0からは、データソースを使った次のようなコードが可能となりました。この方式のポイントは、コードの中にデータベースの情報を直接ハード・コードすることをやめて、そうした情報をJNDIを通じて実行時に獲得しようとすることです。プログラムの中には、データベースのリソースに対応するバーチャルな「名前」が書いてあるのですが、それが実際にはどのようなリソースを指すのかは、プログラムの側ではなく、アプリケーションのdeploy時に設定可能です。
try { |
このコードの先頭のnew InitialContext()というのは、JNDIとの接続に必要なオブジェクトを作り出しています。こうして生成されたオブジェクトに対して名前でlookupをかけると、JNDIはJNDIサーバに登録されているオブジェクトを返します。
J2EEでは、このようにデータベースの接続にJNDIが使われるだけではなく、EJBのHomeインターフェイスを実装したオブジェクトを獲得するのにも、また、JMSでのメッセージングにもJNDIが利用されます。J2EEを構成する個々の要素技術については、いろいろ語られることが多いのですが、JNDIは脇役です。ただ、J2EEで、これらの技術を結び付けているのは、JNDIだと考えることもできます。今度、機会があったらJNDIを縦糸にしてJ2EEを解説してみたいと思っています。イニシャル・オブジェクトを作ってJNDIサーバに接続して、文字列で与えられる名前でルックアップしオブジェクトを獲得するというこのパターンは、J2EEでアプリケーションを書くうえでは必須のテクニックです。
リソース・プロパティ・ファイル |
J2EEでのJNDIを使ったデータソースの利用では、1つ注意してほしいことがあります。
J2EEのバージョンで多少の違いがあるのですが、最新のJ2EE 1.3を例にして説明しようと思います。J2EEをインストールしたディレクトリにconfigという名前のディレクトリがあります。その中に、resource.propertiesというファイルがあると思います(J2EE
1.3以前のバージョンには、このファイルは存在しません。こうしたバージョンでは、以下に述べるデータベースのリソースの定義が、configディレクトリ以下のdefault.propertiesに入っています)。
このファイルは、J2EEサーバの起動時に呼び込まれて、JNDIサーバの初期設定に利用されます。このファイルをよく見ると最初の何行かは、2行で1組になっていることが分かります。最初の行がデータベースにデータソースとしての名前を与え、次の行がそのデータソースの実体をなすデータベースのURLを与えています。この例では、番号0から番号4までの5つのデータソースが定義されています。データソースの名前はそれぞれ異なっているのですが、URLの部分を見ると皆同じことが分かると思います。
ファイル %J2EE_HOME%\config\resource.properties
jdbcDataSource.0.name=jdbc/Cloudscape |
データソースの「2つの名前」 |
このリソース・プロパティ・ファイルの中で、データソースの「名前」と呼ばれているものと、データソースを利用する接続の次のようなコードの中のlookupで使われている「名前」とは、全然一致していませんね。
DataSource datasource = (DataSource) ic.lookup("java:comp/env/jdbc/EstoreDataSource"); |
この不一致は、もともとお互いに関係のないサンプルを2つ突き合わせたから生まれたものではありません。問題は、今回作ろうとしているテーブル・ブラウザのJSPコードの一部とそれが実際に動作する環境のリソース・プロパティ・ファイルとの間にこうした不一致があるのです。
少し、種明かしをしようと思います。まず、J2EEのJNDIサーバは、ユーザーが与えた名前の先頭に必ずjava:comp/env/という文字列を追加します。ですから、lookupの引数に与えられている文字列からこの文字列をさっぴくとjdbc/EstoreDataSourceという文字列が残ります。問題は、この名前がリソース・プロパティ・ファイルの中に存在するかということです。残念ながら、ファイル中の5つの名前の中にはjdbc/EstoreDataSourceという名前は見つかりません。
もう1つ種明かしをしないといけません。実は、J2EEではデータベースは2つのデータソース名を持っているのです。1つは、コンポーネント側のコードで利用される「名前」で、lookupメソッドの呼び出しでは、先頭にjava:comp/env/という文字列を付け加えて利用される「名前」です。もう1つは、リソース・プロパティ・ファイルで定義されて、J2EEサーバの立ち上げ時にJNDIサーバの初期化に利用される「名前」です。
deploy時に、deploytoolで |
前者の「名前」は、リソース参照(Reference)の「コードされた名前(Coded Name)」あるいは、「参照名(Reference Name)」と呼ばれ、後者の名前は、「JNDI名(JNDI Name)」と呼ばれています。deploytoolは、この2つの名前を結び付ける働きを持っています。
Webコンポーネントを作るウィザードの中でもこの指定はできるのですが、ウィザードで指定できるものはdeploytoolの中からでも指定できます。筆者は、ウィザードの指定は、適当なところで切り上げて、あとでdeploytoolのタブを開いてdeployの環境設定をするのが便利だと思っています。
図3を見てください。左側のツリーでWebコンポーネント(エメラルド色のビーンがビンに入っているやつです)を選択して、Resource Refsというタブを開きます。Coded Nameの部分には、JSPの中で、lookupの引数に指定した名前が入っています。最初は、下の段のJNDI Nameの部分はマスクがかかっているのですが、上の列をクリックすると、マスクが外れて入力可能になります。ここでは、リソース・プロパティ・ファイルからjdbc/EstoreDBという名前を選んでいます。
図3 Resource Refsタブを開いたところ |
WebコンポーネントのJNDI Namesタブ(図4)や、アプリケーションのJNDI Namesタブ(図5)でも、この2つの名前の結合が可能です。
図4 WebコンポーネントのJNDI Namesタブを表示したところ |
図5 アプリケーションのJNDI Namesタブを表示したところ |
テーブルの属性をシステム・テーブルから読む |
呼び出し側の処理の大まかな流れは見てきました。今度は、テーブルの属性やテーブルの内容を表示する処理を見ておきましょう。まず、テーブル属性の表示ですが、次のSQL文は、Cloudscapeで、テーブルITEMの項目名、項目のテーブル内の順番、項目のデータ型、また、この項目がNULLの値を取り得るかの情報を表示(図6)します。
SELECT c.COLUMNNAME, c.COLUMNNUMBER, |
図6 ITEMの項目を表示したところ |
いくつか説明が必要です。まず、このSQL文では、SYS.SYSCOLUMNSとSYS.SYSTABLESという2つのテーブルが使われています。このJ2EEアプリケーション自身を使った、2つのテーブルの内容の表示は、次のようなものです(SYS.SYSCOLUMNS(図7)、SYS.SYSTABLES(図8))。SYS.SYSCOLUMNSは、データベース中のすべてのテーブルのすべての項目をまとめたテーブルです。
図7 テーブル「SYS.SYSCOLUMNS」 |
図8 テーブル「SYS.SYSTABLES」 |
テーブルの先頭のREFERENCEID項目は、この項目が属するテーブルのTABLEIDです。この出力の最初の2行のCOLUMNNANEがSCHEMAIDとTABLEIDのREFERENCEIDは80000010-00d0-fd77-3ed8-000a0a0b1900で、SYS.SYSTABLESの出力の2行目の、SYSTABLESのTABLEIDと一致していることが分かりますね。これが先のSQL文でのジョインを説明します。最後の、ORDER BY句は行の出力をテーブル中での項目の順番で並べ替えるものです。
CloudscapeのJava対応拡張機能 |
ジョインや行の整列は、SQLとしては普通のことですが、先のSelect文には、おそらく、皆さんがあまり見たことのないようなシンタックスが含まれています。Selectの後ろの、項目が並ぶ部分の3番目と4番目の次のような表現は、Cloudscape独自のものです。
c.COLUMNDATATYPE.getSQLstring(), c.COLUMNDATATYPE.isNullable() |
SYS.SYSCOLUMNSテーブルの属性(図9)を見てみれば、COLUMNDATATYPEという項目のデータ型はSerializableで、型はCOM.cloudscape.types.TypeDescriptorというように表示されているのが分かりますでしょうか? Cloudscapeでは、テーブルの項目にJavaのオブジェクトが置けるというのが大きな特徴です。この例では、テーブルSYS.SYSCOLUMNSの中の項目COLUMNDATATYPEからJavaのオブジェクトを取り出して、そのオブジェクトに対してメソッドgetSQLstring()とメソッドisNullable()を適用して、その結果をSelect文が返すということになります。
図9 COLUMNDATATYPEE項目のデータ型はSerializableで、型はCOM.cloudscape.types.TypeDescriptorとなっている |
HTMLでテーブルを作る |
HTMLでテーブル(データベースのテーブルではありません)を作るには次のようなタグを使います。tableタグやtrタグは、<table bgcolor="#336666"> <tr bgcolor="#aacd88">のような形でテーブルの背景色を指定することができます。
<table> |
見出し部分に、データベース・テーブルの項目名を置き、データ部分にSelectで検索した結果を置くとWebのページのテーブルができます。これで準備ができました。HTMLのテーブルを出力するJSPコードを見てみましょう。
ここでは、ResultSetのインスタンスに対する、次のようなメソッドrs.getString(i)に注目してください。以前の例ではrs.getString("SCHEMANAME")のような形で、直接に項目名を文字列で指定して項目の値を獲得していましたが、このように、テーブル内での項目の位置を指定して項目の値を得ることもできます。ここでのテーブル内の項目の位置とは、項目に振られたCOLUMNNUMBERにほかなりません。
<table bgcolor="#336666"> ResultSet rs = stmt.executeQuery(
query ); |
四角テーブルについてのメタデータの利用 |
このAttributesの出力テーブルでは、見出しは4つに固定されています。これは、SQLのSelectで、4つの項目の検索を行っていたからです。具体的なテーブルの検索では、テーブルのどの項目を検索しているのかは、たいていの場合は明確です。しかし、テーブルの名前を知っていてもそれ以外の情報を持たない場合もあり得ます。こうした場合、SQLでは、例えばSelect * from SYS.SYSTABLESという形の検索を行います。こうした場合、JDBCのJavaプログラムから検索しているテーブルの項目の名前や項目の数を獲得する方法があります。次のコードを見てください。
query = "Select * from SYS.SYSTABLES"
; ResultSetMetaData meta = rs.getMetaData(); |
ポイントは、ResultSetのインスタンスから、getMetaDataメソッドでResultSetMetaDataという型のインスタンスを獲得しているところです。このメタデータを使えば、getColumnCountメソッドでテーブルの項目数が、また、getColumnLabelメソッドで項目のラベル名を獲得することができます。
Java Pet Storeのイメージ・データを利用する |
これでテーブル・ブラウザの基本的な骨組みはできました。全体のソースと起動の仕方は、最後に述べるとして、ちょっといたずらをしてみましょう。今回のテーブル・ブラウザは、機能としては汎用的ですが、その働きをチェックするためには対象となるデータベースが必要です。一番簡単なのは、J2EEがサンプルを提供しているJava Pet Storeのデータベースを、このテーブル・ブラウザの対象にすることです。
Java Pet Storeデモのデータベースには、ちょっと面白いデータが含まれています。例えば、PRODUCTテーブルのDESCN項目には、次のような形式のデータが含まれています。
<image src="../images/fish1.jpg">Salt
Water fish from Australia |
これは、HTMLのimageタグですね。今回のJ2EEアプリケーションは、もともとデータベースの内容をWebでブラウズすることを目的としたものですから、このテーブル・ブラウザのJSPが置かれたディレクトリの1つ上のディレクトリに、imagesというディレクトリを作って、そこにJPSのイメージ・データを置けば、このテーブル・ブラウザで、JPSの画像を見ることができるはずです。
deploytoolで、アプリケーションに |
JPSのイメージ・データは、最新のjps1.1.2でしたら、次のディレクトリに置かれています。
%JPS_HOME%\src\petstore\src\docroot\images |
deploytoolで、左側のツリーからWebコンポーネントを選び、左のGeneralタブで[Edit...]ボタンをクリックします。そうすると、図10のようなパネルが開きますので、Addボタンを押して上部のパネルでJPSのイメージ・ディレクトリを探し、それを下部のパネルにドロップします。この図は、ドロップが終わってからの状態のものです。Webコンポーネントのどの位置にimagesディレクトリがくればいいのかは、よくチェックしてください。追加が終われば次のような状態になっているはずです(図11)。あとは、Toolsメニューから再配置を選んで[File]メニューで保存しておきます。
図10 [Add]ボタンを押して上部のパネルでJPSのイメージ・ディレクトリを探し、それを下部のパネルにドロップする |
図11 JPSの追加が終わったところ |
このように、deploytoolを利用すれば、J2EEアプリケーションの希望する場所に、簡単にファイルを追加することができます。
イメージ付きのJPSのテーブルにアクセスする |
図12〜14を見てください。これらは、テーブル・ブラウザで、JPSのテーブルPRODUCT、CATEGORY、BANNERDATAをブラウズしてみたところです。
図12 PRODUCTをブラウズしたところ |
図13 CATEGORYをブラウズしたところ |
図14 BANNERDATAをブラウズしたところ |
2/3
|
J2EEの基礎(第5回) | |
テーブル・ブラウザを作る | |
J2EEでのデータベースとの接続 | |
テーブル・ブラウザをインストールし実行する |
連載内容 | |
J2EEの基礎 | |
第1回 Java Pet Storeで、J2EEを体験する(1) | |
第2回 Java Pet Storeで、J2EEを体験する(2) | |
第4回 J2EEアプリケーションを構成するコンポーネント | |
第5回 データベースのブラウザを作る | |
第6回 EJBにおけるコンテナとコンポーネント | |
第7回 J2EEのセキュリティのキホンを知る | |
第8回 J2EEのトランザクション処理 |
連載記事一覧 |
- 実運用の障害対応時間比較に見る、ログ管理基盤の効果 (2017/5/9)
ログ基盤の構築方法や利用方法、実際の案件で使ったときの事例などを紹介する連載。今回は、実案件を事例とし、ログ管理基盤の有用性を、障害対応時間比較も交えて紹介 - Chatwork、LINE、Netflixが進めるリアクティブシステムとは何か (2017/4/27)
「リアクティブ」に関連する幾つかの用語について解説し、リアクティブシステムを実現するためのライブラリを紹介します - Fluentd+Elasticsearch+Kibanaで作るログ基盤の概要と構築方法 (2017/4/6)
ログ基盤を実現するFluentd+Elasticsearch+Kibanaについて、構築方法や利用方法、実際の案件で使ったときの事例などを紹介する連載。初回は、ログ基盤の構築、利用方法について - プログラミングとビルド、Androidアプリ開発、Javaの基礎知識 (2017/4/3)
初心者が、Java言語を使ったAndroidのスマホアプリ開発を通じてプログラミングとは何かを学ぶ連載。初回は、プログラミングとビルド、Androidアプリ開発、Javaに関する基礎知識を解説する。
|
|