はじめに
この連載では、「GlassFish」という製品を利用して、Java言語に親しんでもらうことを目的としています。第4回目の今回は、サンプルWebアプリケーションのカタログ部分の作成を通して、Data Access Object(DAO)パターンやGlassFishの管理コンソールの操作などについて触れていきます。
対象読者
- Javaでなにかしらのアプリケーションを作成したことのある方
- Javaの変数の宣言や、if文・for文・while文の制御文など簡単な文法を知っており、アプリケーションを作成したことのある方
オブジェクト指向プログラミングができなくとも構いません。徐々に学んでいければと考えています。また、学びやすいWebアプリケーションをサンプルとするので、Webアプリケーションとは違った分野を勉強したい方には当連載は向いていません。
本稿で想定する主要技術のバージョン
Java EE 5を使用するため、主な技術要素のバージョンは以下の通りです。EJBに関しては応用編で扱う予定です。
- Servlet:2.5
- JSP:2.1
- JSTL:1.2
- JTA:1.1
- EJB:3.0(応用編)
Webアプリケーションの作成
第3回まで読んでいただいた方はいつになったらGlassFishを使ってWebアプリケーションを作成するんだろうかと感じられたことだと思います。Webアプリケーションは「Hello, World」をブラウザに表示するだけでは何も身につけることができないため、第2回と第3回でWebアプリケーションの基礎である、ServletとJSPについて説明しました。準備は整いました。慌てず、じっくりとWebアプリケーションを構築していきましょう。
架空のネットショップである「芋焼酎酒店」を作成していきます。「芋焼酎酒店」は以下の仕様を満たすものとします。
- 芋焼酎のカタログを表示する
- カタログから銘柄を選択し、買い物かごに入れる
- 買い物かごには購入予定の芋焼酎の銘柄、単価、本数を1明細として銘柄分の行をリストとして表示される
- 買い物かご明細の本数は増減できる。ただし、0本になった場合、買い物かごから削除する
- 買い物かごは明細だけでなく、送料、合計金額を表示する
- 芋焼酎酒店は認証機能を持つ
構築スケジュール
- カタログ作成:
第4回(今回)。DAO(Data Access Object)のスケルトンを作成する。当DAOは銘柄の一覧を返すためのクラス。最初はデータベースにアクセスせず、静的に保持しているオブジェクトのArrayListを返す。ServletはDAOを呼び出し、戻り値をセッション中に埋め込みJSPに渡す。JSPは式言語、JSTLのみを使いカタログをブラウザに表示する。最後にスケルトンとして作成したDAOを実際のデータベースに接続する。 - 買い物かご作成:
第5回、第6回では上記仕様の買い物かごの機能を作成する。買い物かごはセッションオブジェクトに格納し、買い物かごへの追加・削除・更新があるたびに買い物かごテーブルの内容を書き換える。第5回では買い物かごを実装するのに必要なテーブルを準備し、買い物かごへの追加機能を実装する。第6回は削除・更新機能を実装する。 - 認証機能作成:
第7回では認証機能を持たせることで、複数の客の管理を行えるようにする。レルムという概念を紹介するが第7回ではJDBCレルムを使用する。
第7回で入門編の最終回とします。応用編はきりがないくらい話題が多いため、厳選して紹介できればと考えています。JBI(Java Business Integration)まで説明できればと思っています。
作成したものをアプリケーションサーバに配置するにはデプロメント・ディスクリプタ(web.xml)を記述する必要がありますが、その都度説明を追加します。
カタログ作成
ネット上で買い物する場合、カタログを見て気に入ったものがあれば買い物かごに追加します。まずはカタログをブラウザに表示する機能を作成します。カタログは日々変化するものであるためデータベースに格納するのが普通の方法ですが、データベースに接続する方法とブラウザにカタログを表示する方法を同時に説明すると分かりづらいため、最初は、ブラウザにカタログを表示するための情報は固定したものを用意しておき、それをブラウザに表示します。表示が成功した後、カタログに表示する銘柄をデータベースの銘柄テーブルから取得し、その情報をもとにブラウザに表示するようにします。
トップ画面の作成
まずは「芋焼酎酒店」のトップ画面を作成します。第7回で認証画面と入れ替える予定です。サーブレットにアクセスするための画面となります。
第2回では画面を添付しての説明でしたが、今回からは操作や指定の方法が分かりづらい場合以外は、メニューからの操作を記述するにとどめます。第2回でも説明したように、最初にプロジェクトを作成する必要があります。この場合、[ファイル]-[新規プロジェクト]-[Java Web]-[次へ]というように記述します。この操作を行うと[名前と場所]画面が開きます。プロジェクト名に「ImoshochuWeb01」を入力し[次へ]をクリックします。[サーバーと設定]-[フレームワーク]-[完了]と操作します。途中の設定はデフォルトのまま、フレームワークも使用しないのでそのまま[完了]ボタンをクリックすると、左上のペインのプロジェクトタブに「ImoshuchuWeb01」というプロジェクトフォルダが作成されるはずです。名称がnullになっている場合は、nullのアイコンを右クリックし[名前を変更]を選択し[名前を変更]ボタンをクリックすると表示されるようになります。
プロジェクトフォルダの[Webページ]アイコンを右クリック して[新規]-[HTML]と選択すると[名前と場所]画面が表示されます。名前に「ImoshochuWelcome」と入力し、場所はWEB-INFとなっていますが、HTMLは静的ファイルであるため、Webページフォルダを指定します。[完了]ボタンをクリックするとプロジェクトフォルダのWebページの下にImoshochuWelcome.htmlファイルが作成されていることが分かります。index.jspは不要なので削除します。図1がこの時点のプロジェクトフォルダの構成になります。
自動的に作成されたImoshochuWelcome.htmlをダブルクリックするとエディタが開きます。titleとbodyを書き換えたものが図2です。
HTMLを確認するのにGlassFishを起動する必要はありませんが、これから必要になるため図3で説明します
- A:サーバーを起動
- B:サーバーをデバッグモードで起動
- C:サーバーを再起動
- D:サーバーを停止
- E:サーバーの状態を再表示
今のところ、A、C、Dを使えれば大丈夫ですが、より生産性を高めるためにはBやEの使い方も習得する必要があります。NetBeans自体の説明が当連載の目的ではないためBやEの使い方は説明しません。単にHTMLを確認するだけであれば、ImoshochuWelcome.htmlファイルを右クリックし[ファイルを実行]をクリックするだけでGlassFishが起動します。起動のログは図4の通りです。応用編で説明予定のJBI(Java Business Integration)というSOAのフレームワークもスタンバイ状態であることが分かります。
サーバーの起動と同時にWebブラウザが起動し図5のようにImoshochuWelcome.htmlが表示されます。Servletを呼び出す機能もない静的な画面です。カタログを表示するために呼び出すServletができ上がった時点で呼び出すためのタグを追加します。
カタログを作成するためのServletを作成
ImoshochuWeb01プロジェクトを右クリック。[新規]-[サーブレット]-[名前と場所]と操作します。クラス名は「ImoshochuCatalog」と入力し、パッケージには「jp.kawakubo」と入力します。第2回で説明したとおりWebで公開しないのであればパッケージ名はtest01などでも構いません。[次へ]ボタンをクリックすると[サーブレット配備を構成]画面が表示されます。
画面上部に[配備記述子(web.xml)に情報を追加]というチェックボタンがあります。デフォルトのままチェックは入れたままにします。URLパターンもデフォルトのままで構いません。あるいはブラウザのURL上にServletの名前のまま表示させたくないのであれば、別の名前を付けることで実現できます。[完了]ボタンをクリックします。
配備記述子とは、NetBeansなどで作成したものをGlassFishなどのアプリケーションサーバに乗せるためのものです。第2回のServletの説明でServletやJSPなどのWebコンポーネントはWebコンテナ上で動くと記述しましたが、そのWebコンテナ上に乗せるために必要なものです。
トップ画面からServletを呼び出せるようにする
ImoshochuWelcome.htmlからImoshochuCatalogを呼び出すためには、ImoshochuCatalogを図6のように、ImoshochuWelcome.htmlを図7のように書き換えます。両ファイルを保存したのち、HTMLファイルを右クリックし[ファイルを実行]を選択すると図8のように[芋焼酎カタログへ]ボタンが追加されています。このボタンをクリックすると図9の画面が表示され、Servletの呼び出しが成功したことが分かります。
ServletとDAOクラスのインターフェースとなるImoshochuCatalogItemクラスを作成
今回の目標は、ブラウザに芋焼酎のカタログを表示することです。カタログを表示するためにはリストでブラウザに表示するのが一番分かりやすいと考えられます。つまり、ブラウザに結果を返すのはJSPの役割となりますが、JSPに値を渡すときは1つの芋焼酎を表したクラスを用意し、それをArrayListとするのがJSPとしては処理しやすいと思います。この形式にするのはJSTLのc:forEachアクションを使えるからです。
この芋焼酎を表したクラスをImoshochuCatalogItemという名称で作成します。プロパティとして、銘柄名、銘柄読み方、度数、麹、芋の種類、製造元、容量、単価を持たせることにします。図10がImoshochuCatalogItemのクラスです。コンストラクタで全ての値を設定し、プロパティはgetterメソッドのみ用意しています。
001:package jp.kawakubo; 002: 003:/** 004: * 005: * @author tomoharu 006: */ 007:public class ImoshochuCatalogItem { 008: 009: private String name = ""; 010: private String nameKana = ""; 011: private int dosu = 0; 012: private String koji = ""; 013: private String sweetPotatoName = ""; 014: private String manufacturer = ""; 015: private float volume = 0f; 016: private int price = 0; 017: 018: public ImoshochuCatalogItem() { 019: 020: } 021: 022: public ImoshochuCatalogItem(String name, 023: String nameKana, 024: int dosu, 025: String koji, 026: String sweetPotatoName, 027: String manufacturer, 028: float volume, 029: int price) { 030: this.name = name; 031: this.nameKana = nameKana; 032: this.dosu = dosu; 033: this.koji = koji; 034: this.sweetPotatoName = sweetPotatoName; 035: this.manufacturer = manufacturer; 036: this.volume = volume; 037: this.price = price; 038: } 039: 040: /** 041: * @return the name 042: */ 043: public String getName() { 044: return name; 045: } 046: 047: /** 048: * @return the nameKana 049: */ 050: public String getNameKana() { 051: return nameKana; 052: }
疑似DAOクラスを作成
本来であればデータベースに接続するDAOクラスを作成するところですが、入門レベルの時は一度に作成せず、今回作成するような疑似クラス(スケルトン)を用意し、インタフェースがうまく設計されていることを確認することをお薦めします。JSPでImoshochuCatalogItemオブジェクトのArrayListを渡すことでカタログを作成できるか確認した後に本来のDAOクラスを作成しても遅くはありません。もしかしたら、ArrayListを渡してもカタログが作成できないのであれば、DAOクラスの作り直しになってしまいます。
インターフェースを作成
このインターフェースは先述のServletとDAOクラスで受け渡されるものを指しているのではなく、JavaのInterfaceを指しています。インターフェースを導入することにより、実装を置き換えることが容易になります。これは疑似DAOから本来のDAOに置き換えることを容易にしてくれます。また、データソースがリレーショナル・データベース(以降、RDB)ではなくXMLデータベースに置き換えるなどの変更が発生しても、Servlet側は一切修正を加える必要がなくなります。
ImoshochuWeb01を右クリック。[新規]-[その他]-[ファイルの種類を選択]と操作し、カテゴリの[Java]をクリックして、ファイルの種類から[Java インタフェース]を選択します。[名前と場所]画面が表示されます。クラス名は「ImoshochuCatalogDAO」と入力、パッケージ名は「jp.kawakubo」と入力し[完了]ボタンをクリックします。エディタが開き、図11のように修正します。引数を持っていませんが、カタログに検索機能を持たせたい場合などは、引数として検索条件を渡すことになります。このサンプルではデータベースの内容をそのまま出力するものとします。
001:package jp.kawakubo; 002: 003:import java.util.ArrayList; 004: 005:/** 006: * 007: * @author tomoharu 008: */ 009:public interface ImoshochuCatalogDAO { 010: 011: public ArrayList getImoshochuCatalogList(); 012: 013:}
インターフェースを実装した疑似DAOクラスを作成
図12がimoshochuCatalogDaoインターフェースを実装した疑似DAOクラスです。コンストラクタでカタログの明細に相当するImoshochuCatalogItemオブジェクトを作成し、ArrayListに追加します。実装するgetImoshochuCatalogListは単にこのArrayListを返すだけです。どうでしょう、至ってシンプルだと思いませんか。このようなクラスを用意することでインターフェースの変更という大きなリスクを軽減することができます。
001:package jp.kawakubo; 002: 003:import java.util.ArrayList; 004: 005:/** 006: * 007: * @author tomoharu 008: */ 009:public class ImoshochuCatalogDAOImpl implements ImoshochuCatalogDAO { 010: 011: private ArrayList imoCatalog = 012: new ArrayList(); 013: 014: public ImoshochuCatalogDAOImpl() { 015: 016: imoCatalog.add(new ImoshochuCatalogItem("がんこ焼酎屋", 017: "がんこしょうちゅうや", 25, "白", "ジョイホワイト", "大石酒造", 1.8f , 2630)); 018: imoCatalog.add(new ImoshochuCatalogItem("貴心樹", 019: "きしんじゅ", 25, "黒", "黄金千貫", "オガタマ酒造", 1.8f , 1724)); 020: imoCatalog.add(new ImoshochuCatalogItem("鴨神楽", 021: "かもかぐら", 25, "白", "黄金千貫", "小牧醸造", 1.8f , 2930)); 022: imoCatalog.add(new ImoshochuCatalogItem("一壺春", 023: "いっこしゅん", 25, "白", "ジョイホワイト", "古澤醸造", 0.72f , 1360)); 024: imoCatalog.add(new ImoshochuCatalogItem("杜氏潤平紅芋原酒", 025: "とうじじゅんぺいべにいもげんしゅ", 38, "白", "紅芋コトブキ", "小玉醸造", 0.5f , 2050)); 026: imoCatalog.add(new ImoshochuCatalogItem( 027: "宝山蒸撰綾紫酒精乃雫", "ほうざんじょうせんあやむらさきしゅせいのしずく", 028: 34, "白", "南薩産綾紫", "西酒造", 0.72f , 2050)); 029: imoCatalog.add(new ImoshochuCatalogItem("角玉", 030: "かくたま", 25, "黒", "黄金千貫", "佐多宗二商店", 1.8f , 2050)); 031: imoCatalog.add(new ImoshochuCatalogItem("古の千鶴", 032: "いにしえのちづる", 25, "黒", "黄金千貫", "神酒造", 1.8f , 2625)); 033: 034: } 035: 036: public ArrayList getImoshochuCatalogList() { 037: return imoCatalog; 038: } 039: 040:}
上記リストでimoCatalogのaddメソッドは途中で改行していますが、実際には1行で記述してください。
Servletを修正
一応、DAOクラスができました。JSPにDAOクラスの戻り値を返すにはHttpSessionクラスを使用します。第3回のJSPの基礎のスコープで一度出てきたクラスです。その時はJSPの変数にアクセスできるスコープとして暗黙オブジェクトのsessionを紹介しましたが、その型がHttpSessionクラスです。他のクライアント同士の情報を隔離したい場合に使用します。DAOクラスからの戻り値をHttpSessionに設定し、RequestDispatcherのforwardメソッドでカタログを表示するJSPへ遷移します。図13はServletのprocessRequestのみを抜粋したものです。
001:protected void processRequest(HttpServletRequest request, 002: HttpServletResponse response) 003:throws ServletException, IOException { 004: // DAOクラスをインスタンス化する 005: ImoshochuCatalogDAO imoshochuCatalogDAO = new ImoshochuCatalogDAOImpl(); 006: // requestからHttpSessionを取り出し、ImoshochuCatalogItemのArrayList 007: // を"imoCatalog"として埋め込む 008: request.getSession().setAttribute("imoCatalog", 009: imoshochuCatalogDAO.getImoshochuCatalogList()); 010: 011: // ImoshochuShop.jspに遷移する 012: RequestDispatcher rd = request.getRequestDispatcher("/ImoshochuShop.jsp"); 013: rd.forward(request, response); 014: 015:}
芋焼酎酒店のJSPを作成
図14が芋焼酎酒店のJSPであるImoshochuShop.jspです。図15がその出力結果です。JSP 1.Xを知っている方はこのシンプルさに感動されるのではないでしょうか。JSLTのc:forEachアクションを使用するだけで、ほぼコーディングが完成してしまいます。JSP 1.XであればsessionのgetAttributeメソッドで"imoCatalog"を指定し、Servletで設定したArrayListを取得し、for文でループを回していました。今回はアクション要素すら使用していません。式言語とJSTLだけを使ってtableを生成することができました。式言語やJSTLの威力を感じていただけたでしょうか?
c:forEachアクションについて少し詳しく解説します。
c:forEachアクションのitems属性には配列やCollectionクラスを継承するものを指定できます。この例ではArrayListが${sessionScope.imoCatalog}で返され、ArrayListはCollectionクラスを継承しているためitemsで指定することが可能です。
また、sessionScopeは変数がどこにあるのかを指定するのに用います。sessionScopeは文字通りsessionのスコープ内にある変数を指す場合に使用する式言語の暗黙オブジェクトです。第3回でスコープについて説明しましたが、session以外に、application、request、pageのスコープがありました。それぞれに対応するのはapplicationScope、requestScope、pageScopeとなります。
次にvar属性は、itemsの要素が格納されます。この例ではImoshochuCatalogItemオブジェクトが格納されますが、属性値は分かりやすい名前であれば何でも構いません。この例ではmeigaraという名前をImoshochuCatalogItemオブジェクトに付けています。このオブジェクトのプロパティから値を取り出したい場合、${meigara.name}のようにvar属性値に.(ドット)でプロパティ名を連結するだけです。当然、nameプロパティはgetterメソッドがなければなりません。第3回の式言語で説明したように型変換を行う必要がないのも便利です。(テンプレート・テキスト内の式言語の結果はStringに型変換されます)。
カタログを確認するにはImoshochuWelcome.htmlを右クリックして[ファイルを実行]を選択すると図8の画面が表示され、その中の[芋焼酎カタログへ]ボタンをクリックすると図15の画面が表示されます。
疑似DAOクラスから本物のDAOクラスを作成
上記の説明で、銘柄1つ分に相当するImoshochuCatalogItemのArrayListをDAOクラスとServletのインターフェースとすると、JSPで式言語とJSTLのみでカタログを表示させることができました。残るは疑似的に作成したImoshochuCatalogDaoクラスから実際のデータベースにアクセスするDAOクラスの作るのみとなりました。
筆者の自宅のデータベースはもっぱらMySQLを使用しています。MySQLを使うとなると、MySQLやMySQL GUI ToolsなどのダウンロードやそれらをNetBeansで使用できるようにするための手順を説明しなくてはなりません。悩んでいたところNetBeansの「MySQL データベースへの接続」というページがありました。非常に分かりやすく書かれているため、MySQLとNetBeansの接続は当サイトを参考にしてください。ただし、英語版のNetBeans 6.8をベースとした説明であることや設定すべきものが何か分かるづらいところだけ説明します。
MySQLやMySQL GUI Toolsは先述のサイトを参考にダウンロード・インストールしてください。インストールする際に入力したパスワードなど忘れないようにメモに残しておきましょう。以下にNetBeansにMySQLを接続するための手順を説明します。
サービスウィンドウを開く
通常、NetBeansの左上にプロジェクトウィンドウがあります。ImoshochuWeb01プロジェクトが作成されているウィンドウです。そのペインにファイルやkenaiウィンドウも存在していると思います。もし、サービスウィンドウがない場合、[ウィンドウ]-[サービス]と操作すると先ほどのペインにサービスウィンドウが表示されます。そのウィンドウには「データベース」「Web サービス」「サーバー」「Hudson ビルダ」「課題追跡」が表示されているはずです。この中の[データベース]を右クリックし[MySQL サーバーを登録]を選択します。[MySQL サーバープロパティー]画面が表示されます。
基本プロパティーを設定
[基本プロパティー]タブを選択すると図16のような画面が表示されます。各設定内容は以下の通りです。
- サーバーホスト名:ホスト名またはIPアドレスを入力します。IPアドレスの場合は「192.168.1.10」のように入力します。
- サーバーポート番号:MySQLの場合、3306がデフォルトのためそのままにします。
- 管理者のユーザー名:MySQLのインストール時に指定した管理者名を入力します。
- 管理者のパスワード:MySQLのインストール時に指定した管理者のパスワードを入力します。ここでメモしたものが役立ちます。学習する分にはパスワードに「root」や「password」など入力しても構いません。業務で使用する場合もこのようなパスワードを使用しているのを何度も見かけていますが、セキュリティ上絶対避けるべきことのひとつです。データベースのパスワードは企業情報を守る最後の砦です。推測不能なパスワードを入力してください。
管理プロパティーを設定
[基本プロパティー]タブの右隣にある[管理プロパティー]をクリックします。図17が管理プロパティーの設定画面です。各設定内容は以下の通りです。
- 管理ツールのパスまたはURL:MySQL GUI Toolsでダウンロードしたフォルダの中のMySQLWorkbench.exe(Windowsの場合)のパスを指定します。
- 起動コマンドのパス:これはMySQL自体の起動コマンドを指定します。インストールしたフォルダ配下のbinフォルダにあるmysqld.exe(Windowsの場合)を指定します。
- 停止コマンドのパス:これもMySQL自体の停止コマンドを指定します。上記と同じコマンドではなく、mysqladmin.exe(Windowsの場合)のパスを指定します。
- 引数:mysqladmin.exeの場合、停止するには引数が必要となります。「-u root stop」と入力してください
データベースを作成
[MySQL サーバーを登録]を完了するとデータベースの下に「MySQL サーバー localhost:3306 [root]」というノードが生成されているはずです。生成されていれば、MySQLサーバとNetBeansの接続が成功したことになります。
「MySQL サーバー localhost:3306 [root]」を右クリックし[データベースを作成]を選択すると[MySQL データベースの作成]画面が表示されます。新規データベース名に「imoshop」と入力します。「MySQL サーバー localhost:3306 [root]」ノード配下にデフォルトで存在する「information_schema」「mysql」「test」と同じレベルに「imoshop」ができているはずです。MySQL Workbenchを使用するには「MySQL サーバー localhost:3306 [root]」ノードを右クリック、「管理ツールを実行」を選択すると、MySQL Workbenchが起動します。使い方はMySQL Documentation:MySQL Workbenchのサイトを参考にしてください(日本語版が見つかったら追記します)。
meigaraテーブルを作成します。カラムはid(主キー)、name、nameKana、dosu、koji、sweetPotatoName、manufacturer、volume、priceです。
「insert into meigara(name, nameKana, dosu, koji, sweetPotatoName, manufacturer, volume, price) values('がんこ焼酎屋', 'がんこしょうちゅうや', 25, '白', 'ジョイホワイト', '大石酒造', 1.8 , 2630);」のSQL文を使用してimoshochuCatalogItemのコンストラクタで追加した銘柄の数だけ作成します。変えるところは、values以降の()の部分です。meigaraテーブルをつくるのが面倒な方のために、サンプルコードにimoshop.sqlがあります。MySQL Workbenchなどを使用してテーブルを作成してください。
ImoshochuCatalogDAOImplクラスを修正
図18がJDBCのAPIを使ってデータベースにアクセスし、その戻り値をArrayListとして返すgetImoshochuCatalogListメソッドを持つクラスです。このクラスを作成するには12行目のcom.mysql.jdbc.Driverクラスを含むjarファイルをライブラリに追加する必要があります。[プロジェクト]-[ImoshochuWeb01]-[ライブラリ]と操作し、右クリックし[ライブラリを追加]を選択すると「ライブラリを追加」画面が開きます。[大域ライブラリ]を開き[MySQL JDBC ライブラリ]を選択します。[ライブラリを追加]ボタンをクリックすると、ライブラリにmysql-connector-java-5.1.6-bin.jarが追加されます。これでcom.mysql.jdbc.DriverクラスをClassのforNameメソッドでロードすることが可能になります。
001:public class ImoshochuCatalogDAOImpl implements ImoshochuCatalogDAO { 002: 003: private final static String url = "jdbc:mysql://localhost:3306/imoshop"; 004: private final static String sql = "select * from meigara"; 005: 006: public ArrayList getImoshochuCatalogList() { 007: Connection conn = null; 008: Statement statement = null; 009: ResultSet rs = null; 010: ArrayList imoCatalog = new ArrayList(); 011: try { 012: Class.forName("com.mysql.jdbc.Driver"); 013: conn = DriverManager.getConnection(url, "root", "●●●●"); 014: statement = conn.createStatement(); 015: rs = statement.executeQuery(sql); 016: ImoshochuCatalogItem imoItem = null; 017: while(rs.next()) { 018: imoItem = new ImoshochuCatalogItem(); 019: imoItem.setName(rs.getString("name")); 020: imoItem.setNameKana(rs.getString("nameKana")); 021: imoItem.setDosu(rs.getInt("dosu")); 022: imoItem.setKoji(rs.getString("koji")); 023: imoItem.setSweetPotatoName(rs.getString("sweetPotatoName")); 024: imoItem.setManufacturer(rs.getString("manufacturer")); 025: imoItem.setVolume(rs.getFloat("volume")); 026: imoItem.setPrice(rs.getInt("price")); 027: imoCatalog.add(imoItem); 028: } 029: } catch(SQLException se) { 030: se.printStackTrace(); 031: } catch(Exception e) { 032: e.printStackTrace(); 033: } finally { 034: try { 035: if (statement != null) { 036: statement.close(); 037: } 038: } catch(SQLException se) { 039: se.printStackTrace(); 040: } 041: try { 042: if (rs != null) { 043: rs.close(); 044: } 045: } catch(SQLException se) { 046: se.printStackTrace(); 047: } 048: try { 049: if (conn != null) { 050: conn.close(); 051: } 052: } catch(SQLException se) { 053: se.printStackTrace(); 054: } 055: } 056: return imoCatalog; 057: } 058:}
データベースへは、ドライバクラスをロードし(12行目)、DriverManagerのgetConnectionメソッドを使い(13行目)接続します。getConnectionメソッドの第1引数はimoshopデータベースのURL、第2引数と第3引数は、データベースへ接続するのに認証が必要な場合設定する必要があります。第2引数はユーザ名、第3引数がパスワードです。これらは図16で設定したものです。
データベースへ接続後、SQL文を実行するには、Statementクラスを使います。4行目がSQL文ですが、プレースフォルダを持たないSQL文の場合、Statementクラスを使用します。プレースフォルダを使用する場合、PreparedStatementクラスを使います。PreparedStatementクラスやプレースフォルダは次回以降に説明します。13行目でConnectionオブジェクトを取得し、14行目でそのオブジェクトからStatementオブジェクトを取得します。15行目でStatementオブジェクトのexecuteQueryメソッドの引数にSQL文を設定し呼び出すと、データベースへ検索を行い結果をResultSetオブジェクトで返します。ResultSetからデータを取り出す処理が19行目から27行目です。取りだす値の型によりメソッドも異なります。String型、Int型、Float型ではそれぞれgetString、getInt、getFloatメソッドを使用します。引数はmeigaraテーブルで定義したカラム名です。17行目から29行目がImoshochuCatalogItemを作りArrayListに追加する処理です。
注意しなければならないのは、Statement、Connection、ResultSetオブジェクトはリソースを消費するため、使い終わったらcloseメソッドでリソースの解放を行わなければなりません。したがって、これらのリソースを解放する処理を33行目以下のfinally節で行っています。try節の中でclose処理を行えばいいように思えますが、Statement、Connection、ResultSetの順でclose処理を行うとした場合、Statementのクローズ処理に失敗すると、ConnectionやResultSetのクロース処理が行われないため、Exceptionの発生に関係なく実行されるfinally節の中でcloseするのが好ましいと筆者は考えています。ただし、finally節においても例外が発生する場合は図18の39、46、53行目のようにprintStackTraceでログに出力するか、Exceptionクラスをthrowするかは例外処理の設計次第となります。主にソフトウェアアーキテクトが行う仕事です。ここで大切なのはリソースの解放です。Javaはガベージコレクションがあるからメモリ管理は不要だと入門者の方は勘違いしやすいですが、ガベージコレクションの対象となるのは、オブジェクトがnullか、上記オブジェクトのように明示的にclose処理を行ったものが対象となります。一番メモリリークが発生しやすいところなので注意が必要です。
最後にreturn文によりImoshochuCatalogItemのArrayListであるimoCatalogを呼び出し元に返しています。これ以降の処理の流れは全く同じであるため、省略します。
GlassFishの管理コンソールを操作
そろそろGlassFishの説明がないのをご不満に感じられていると思います。具体的な例を交えたかったためここまで説明を引き延ばしてしまいました。DAOクラスの説明でConnectionクラスの取得方法を紹介しましたが、Servletのスレッドプール同様、Connectionもプールしておくことにより、Connectionの生成に要するコストを削減したいと思います。
管理コンソールを開く
GlassFishの管理コンソールを開くには[サービス]-[サーバー]-[GlassFish v2.1]を選択し右クリックします。[管理コンソールを表示]を選択するとブラウザが開き管理コンソール画面が開きます。メモリが不足しているときは開かない場合があります。使っていないものを閉じてから再度開いてください。図19が管理コンソールの認証画面です。ユーザ名、パスワードは第1回でGlassFishのセットアップで設定したものを入力します。図20は管理コンソールの左ペインです。図21は右ペインです。基本的に左ペインのノードを選択すると右ペインに設定画面が表示されます。ただし、慣れないうちは右ペインに最初に表示される[共通操作]画面から目的の操作を選択することをお薦めします。
コネクションプーリングを使用可能にする
コネクションとはアプリケーションサーバとデータベースとの接続に他なりませんが、それをクラス化したものがConnectionクラスです。一般にアプリケーションとデータベースを接続するには時間がかかります(時間がかかると言うことはリソースを大量に消費しているということであり、そういう意味で「コストのかかる処理」などと呼ばれます)。コネクションの場合も、あらかじめコネクションを生成・プールしておき、アプリケーションからコネクションの要求があれば、空いているコネクションを渡すという方法をとります。このようなプールをコネクションプール(接続プール)と呼び、仕組みをコネクションプーリングと呼びます。アプリケーションからコネクションプーリングを使用してコネクションを得るには以下の手順を踏みます。
- GlassFishの管理コンソールを用いてコネクションプールを作成する
- GlassFIshの管理コンソールを用いてコネクションプールにJDNI名を割り当てる
- web.xml、sun-web.xmlでコネクションプールを定義する
- JNDIのAPIを用いてDataSourceオブジェクトを取得する
- DataSourceオブジェクトからConnectionオブジェクトを取得する
GlassFishの管理コンソールを用いてコネクションプールを作成する
左ペインで[リソース]-[JDBC]と開き、[接続プール]を開くと図22のようにデフォルトの4つのコネクションプールが表示されます。[新規]ボタンをクリックすると、図23のように[新しいJDBC接続プール(ステップ 1/2)]画面が開きます。名前に「ImoshopPool」を入力し、リソースタイプは「javax.sql.DataSource」を選択します。[次へ]ボタンをクリックすると、図24のように[新しいJDBC接続プール(ステップ 2/2)]画面が表示されます。データソースクラス名に「com.mysql.jdbc.jdbc2.optional.MysqlDataSource」を入力します。実際の画面には「プール設定」、「接続検証」、「トランザクション」などの多くの設定項目があり、プロジェクトの特性に応じてチューニングする必要があります。今回はデフォルト値のまま[完了]ボタンをクリックします。図25のようにImoshopPoolが追加されているのが分かります。
[新しいJDBC接続プール(ステップ 2/2)]画面でデータソースクラス名だけ入力し完了しましたが、実際は一番下にある[プロパティーを追加]ボタンをクリックし、接続に必要なプロパティーを指定する必要があります。ただし、プロパティーの追加はいつでも可能です。図25のコネクションプール一覧から作成したImoshopPoolのリンクをクリックすると図26のように[接続プールを編集]画面が表示され、一番右側の[追加プロパティー]をクリックし、[プロパティーを追加]ボタンをクリックすることでプロパティーの設定ができます。接続に必要なプロパティーは図27の通りです。
GlassFIshの管理コンソールを用いてコネクションプールにJDNI名を割り当てる
プログラムの中から直接コネクションプールを呼び出すことはできません。これらのリソースはJNDIというAPIを使用して呼び出します。最近ではRMIなど使わないため、JNDIと言えばLDAPを扱うためのAPIと思っている方も多いと思います。また、外部の(当連載の例でいえばWebアプリケーションの外側)のリソースを扱うためのAPIがJNDIであるという説明もよく見かけます。こちらの定義の方がコネクションプールの場合に当てはまると思います。データベースはWebアプリケーションに従属する存在ではありません。他のWebアプリケーションを作成し、ImoshopPoolを使いたい場合もあると思います。したがって、Webアプリケーションとコネクションプールは独立した存在であることが分かります。このような意味で言えば外部のリソースということになります。さらにはマシンの境界さえ越える場合もあります。他のマシンからオブジェクトを取得したい場合、severNameプロパティーは「localhost」ではなくサーバの名称かIPアドレスを指定します。
前置きが長くなりましたが、コネクションプールをJNDI名に割り当てます。操作は簡単で[接続プール]のすぐ上にある[JDBCリソース]をクリックします。図28のようにデフォルトのJDBCリソースが表示されます。[新規]をクリックすると図29のように[新しいJDBCリソース]画面が表示されます。JNDI名に「jdbc/imoshop」と入力し、プール名はリストから作成したコネクションプールであるImoshopPoolを選択します。[了解]ボタンをクリックするとコネクションプールとJNDI名が結び付きます。
web.xml、sun-web.xmlでコネクションプールのJNDI名を指定する
図30がweb.xml、図31がsun-web.xmlです。web.xmlのresource-ref要素の内容がコネクションプールのJDNI名の指定になっています。
JNDIのAPIを用いてDataSourceオブジェクトを取得する
図32の7行目、8行目でコネクションプールのJDNI名を元にDataSourceを取得しています。Contextオブジェクトのlookupメソッドが外部リソースを探すためのメソッドです。引数はJNDI名を設定しますが、管理コンソールで入力した「jdbc/imoshop」だけでは認識してくれません。頭に「java:comp/env/」をつける必要があります。アノテーションを使えるアプリケーションサーバもあるようですが、GlassFish v2.1.1ではDataSourceを得ることができず、従来通りのコーディングをしています。(アノテーションの場合、DataSourceの宣言の前に
@Resource(name="jdbc/imoshop")
のように管理コンソールで入力したJNDI名がそのまま使えます。今回は、外部ソースを見つけることが目的であるためアノテーションを使えなくても構いませんが、成功した方がいらっしゃればコメント頂ければと思います。
001: public ArrayList getImoshochuCatalogList() { 002: Connection conn = null; 003: Statement statement = null; 004: ResultSet rs = null; 005: ArrayList imoCatalog = new ArrayList(); 006: try { 007: Context c = new InitialContext(); 008: DataSource ds = (DataSource)c.lookup("java:comp/env/jdbc/imoshop"); 009: conn = ds.getConnection(); 010: statement = conn.createStatement(); 011: rs = statement.executeQuery(sql); 012: ImoshochuCatalogItem imoItem = null;
DataSourceオブジェクトからConnectionオブジェクトを取得する
図32の9行目でConnectionオブジェクトを取得しています。コネクションの取得が完了すれば、残りの処理は全く同じです。
おさらい
- 第3回で説明した式言語、JSTLを使ってカタログを作成
- MySQL、MySQL Workbenchのインストールとセットアップ
- GlassFishの管理コンソールを使ってコネクションプールを作成
MySQL WorkbenchとGlassFishの管理コンソールについて体系的に説明することは当連載の目的ではないため、ユーザガイドなどをダウンロードし学習していただければと思います。
サンプルコードとimoshopデータベースのリストア用SQLはページトップに用意しています。ダウンロードしてお使いください。今回のプロジェクトのフォルダ構成は図33の通りです。リストア用SQLはMySQL Administratorのexport機能を使用して作成したものです。MySQL Administratorのimportを使用してデータベースを作成してください。今回までは、非常に簡単なテーブルなので、SQL文の中を見て、皆さんがお使いのデータベース製品に作成されても構いません。なお、ImoshochuCatalogDAOImplについては最終的な第2版のみ収録していますのでご了承ください。
次回は芋焼酎酒店の買い物かごの実装をします。








![図8.トップ画面の[芋焼酎カタログへ]ボタンの確認](img/glassfish_Console-/GlassFish_4_108_s.gif)









![図23.[新しいJDBC接続プール(ステップ 1/2)]画面](img/glassfish_Console-/GlassFish_4_123_s.gif)
![図24.[新しいJDBC接続プール(ステップ 2/2)]画面](img/glassfish_Console-/GlassFish_4_124_s.gif)

![図26.[接続プールを編集]画面](img/glassfish_Console-/GlassFish_4_126_s.gif)


![図29.[新しいJDBCリソース]画面](img/glassfish_Console-/GlassFish_4_129.gif)


