データベースデータからのツリーの構築
執筆: NetBeans チュートリアルチーム
| 2007 年 12 月 [リビジョン番号: V6.0] |
|
|
このチュートリアルでは、データベース内のデータからツリー構造を動的に構築する方法について説明します。NetBeans IDE 6.0 を使用して、2 ページ構成のアプリケーションを構築し、最初のページにツリーコンポーネントを配置します。ツリーの第 1 レベルノードにはデータベースから取得した旅行者の名前を表示し、第 2 レベルノードにはその旅行者の旅行情報を表示します。旅行情報のノードは、旅行の詳細を示す 2 ページ目にリンクします。
|
|
目次
|
|
|
このチュートリアルでは、次のテクノロジとリソースを使用します。
JavaServer Faces コンポーネント /
Java EE プラットフォーム |
Java EE 5* の場合、1.2
J2EE 1.4 の場合、1.1
|
| Travel データベース |
必須 |
* NetBeans IDE 6.0 の Java EE 5 機能を活用するためには、Sun Java System Application Server 9 (GlassFish Project) などの、Java EE 5 仕様に完全に準拠したアプリケーションサーバーを使用してください。
このチュートリアルは、GlassFish v2 Application Server を使用することを想定しています。別のサーバーを使用している場合は、「リリースノート」と「FAQ」で既知の問題やその回避策を確認してください。サポートされているサーバーと Java EE プラットフォームについては、「リリースノート」を参照してください。
ホームページのデザイン
最初に、ツリーコンポーネントと TRIP データベース表を含むホームページを作成します。作成するページは次の図のようになります。

図 1: ページデザイン |
-
新しい Visual Web JSF アプリケーションプロジェクトを「
DatabaseTree」という名前で作成し、Visual Web JavaServer Faces フレームワークを有効にします。
-
「パレット」の「基本」セクションからページに「ツリー」コンポーネントをドラッグし、「旅行情報」と入力して、Enter キーを押します。「プロパティー」ウィンドウで、「id」プロパティーを「displayTree」に、「clientSide」プロパティーを「True」に設定します。
clientSide を True に設定すると、すべての子ノードが、展開しているかどうかにかかわらずクライアントに送信されます。ただし、親ノードを展開しないかぎり子ノードは表示されません。clientSide を False に設定すると、展開している親ノードの子ノードだけが送信されます。
-
「ツリーノード 1」を選択して右クリックし、ポップアップメニューから「削除」を選択します。
このアプリケーションでは、プログラムでツリーに値を表示するので、IDE によって作成される初期のツリーノードは必要ありません。ノードを削除しないと、JSP タグの属性に設定された値が実行時の設定に優先して、ページにノードが表示されます。
-
「パレット」から「メッセージグループ」コンポーネントをページ上の右上隅にドラッグします。
データベースへの接続
ここでは、Travel データソースのデータベース表にページを接続します。次に、旅行者の名前がアルファベット順に表示され、旅行の出発日が日付順に表示されるように、クエリーエディタを使用して、データを取得する SQL クエリーを編集します。
-
「サービス」ウィンドウを開き、「データベース」ノードを展開して、Travel データベースが接続されていることを確認します。
Travel データベースのバッジの jdbc ノードが壊れていてノードを展開できない場合、IDE は、このデータベースに接続されていません。Travel データベースに接続するには、Travel の jdbc ノードを右クリックし、ポップアップメニューから「接続」を選択します。「接続」ダイアログが表示された場合は、「パスワード」に「travel」と入力し、「セッション中はパスワードを保存」を選択して、「了解」をクリックします。
注: Apache Tomcat を使用している場合は、データベースに接続する前に、derbyClient.jar ファイルを <tomcat_install>/common/lib ディレクトリにコピーします。
-
次の図に示すように、TRAVEL データベースの jdbc ノードを展開し、「表」ノードを展開します。

図 2: Travel データベース |
「TRIP」ノードをビジュアルデザイナ上にドラッグします。
「ナビゲータ」ウィンドウの「Page1」セクションに「tripDataProvider」ノードが表示され、「SessionBean1」セクションに「tripRowSet」ノードが表示されます。
-
「ナビゲータ」ウィンドウで SessionBean1ノードを展開し、tripRowSet ノードを右クリックして「SQL 文を編集」を選択します。
編集領域にクエリーエディタが表示され、TRIP 表の図が表示されます。
-
「サービス」ウィンドウから「Travel」>「表」>「PERSON」ノードをドラッグし、図 3 に示すように、クエリーエディタの TRIP 表の図の横にドロップします。
別の表の図が表示されます。2 つの表の図がリンク (結合) された状態になっています。
- PERSON 表内の PERSONID のチェックボックスの選択を解除します。
-
クエリーエディタのデザイングリッドで、TRAVEL.PERSON 表の NAME 行を探します。「ソート方法」セルをクリックし、ドロップダウンリストから「昇順」を選択します。
この操作により、データベース表内の名前が姓のアルファベット順でソートされます。
-
TRAVEL.TRIP 表の DEPDATE 行を探します。「ソート方法」セルをクリックし、ドロップダウンリストから「昇順」を選択します。
この操作により、旅行の出発日が古い日付から新しい日付の順でソートされます。クエリーエディタは次の図のようになります。

図 3: クエリーエディタ |
データベース表からのツリーの構築
ここでは、アプリケーションの両方のページで使用する情報を格納する要求 Bean プロパティーを追加します。次に、TRIP と PERSON の各データベース表からツリーコンポーネントを動的に構築するコードを prerender() メソッドに追加します。
-
Page1 を開いて「ナビゲータ」ウィンドウを表示します。「ナビゲータ」ウィンドウで、RequestBean1 ノードを右クリックして「Java ソースを編集」を選択します。
-
コンストラクタ public class RequestBean1 extends AbstractRequestBean の下で、プロパティーを次のように宣言します。
private Integer personId;
- Java エディタを右クリックし、「リファクタリング」>「フィールドをカプセル化」を選択します。
- 「フィールドをカプセル化」ダイアログで、次の図に示すように、取得メソッドおよび設定メソッドを作成するボックスをチェックします。フィールドの可視性に対する変数の宣言は非公開で、アクセス用の可視性に対する変数の宣言は公開であることを確認し、「リファクタリング」をクリックします。
図 3: テキストファイルのアップロード |
-
Java エディタで Page1 を開き、 prerender メソッドまでスクロールします。prerender メソッドの本体を次のコード ( 太字 部分) に置き換えます。
| コード例 1: Page1 の prerender メソッド |
public void prerender() {
// 要求 Bean の personId が設定されている場合は、
// Trip ページから戻ったところであり、
// 選択された旅行は表示済み。
// あとで personId を使用して、旅行者のノード
// を展開するかどうかを判断
Integer expandedPersonId = getRequestBean1().getPersonId();
try {
// 必要な変数を設定
Integer currentPersonId = new Integer(-1);
// nbrChildren が 0 以外の場合、これは
// ポストバックであり、ツリーは完成済み
int nbrChildren = displayTree.getChildCount();
if (nbrChildren == 0) {
// 外側 (旅行者) のノードの一覧
List outerChildren = displayTree.getChildren();
// 前の内容を消去
outerChildren.clear();
// 内側 (旅行) のノードの一覧
List innerChildren = null;
// SQL クエリーを実行
tripDataProvider.refresh();
// 結果セットの各行に対して繰り返す。
// 新しい旅行者が見つかるたびに、第 1 レベルのノードを追加。
// 第 2 レベルの旅行ノードを旅行者の親ノードに追加。
boolean hasNext = tripDataProvider.cursorFirst();
while (hasNext) {
Integer newPersonId =
(Integer) tripDataProvider.getValue(
"TRIP.PERSONID");
if (!newPersonId.equals(currentPersonId)) {
currentPersonId = newPersonId;
TreeNode personNode = new TreeNode();
personNode.setId("person" + newPersonId.toString());
personNode.setText(
(String)tripDataProvider.getValue(
"PERSON.NAME"));
// 要求 Bean から旅行者の ID が渡されたら、
// その旅行者のノードを展開
personNode.setExpanded(newPersonId.equals
(expandedPersonId));
outerChildren.add(personNode);
innerChildren = personNode.getChildren();
}
// 新しい旅行ノードを作成
TreeNode tripNode = new TreeNode();
tripNode.setId("trip" +
tripDataProvider.getValue("TRIP.TRIPID").toString());
tripNode.setText(
tripDataProvider.getValue("TRIP.DEPDATE").toString());
tripNode.setUrl("/faces/Trip.jsp?tripId=" +
tripDataProvider.getValue("TRIP.TRIPID").toString());
innerChildren.add(tripNode);
hasNext = tripDataProvider.cursorNext();
}
}
} catch (Exception ex) {
log("Exception gathering tree data", ex);
error("Exception gathering tree data: " + ex);
}
}
|
このコードにより、personId 順に並べられている旅行レコードが読み取られます。personId ごとに新しい第 1 レベルのノードがツリー内に作成されます。次に、その personId に関連付けられている旅行ごとに、第 2 レベルのノード (下位ノード) が作成されます。最後に、第 2 レベルの旅行ノードが、あとで作成する tripNode_action メソッドにバインドされます。
- ソース内を右クリックし、ポップアップメニューから「インポートを修正」を選択し、クラスが見つからないというエラーを修正します。「すべてのインポートを修正」ダイアログで、「リスト」フィールドに
java.util.List が表示されていることを確認し、「了解」をクリックします。
プロジェクトを実行します。
Web ブラウザが開き、次の図に示すように、それぞれ旅行者の名前を示す第 1 レベルのノードごとのツリーコンポーネントが表示されます。ノードを展開すると、その旅行者の旅行の出発日が表示されます。図に示すように、旅行者の名前は姓のアルファベット順、旅行の出発日は日付順に表示されます。次の節では、ユーザーが旅行ノードをクリックしたときに 2 ページ目に移動するためのコードを追加します。2 ページ目には、ユーザーが選択した旅行の詳細が表示されます。
図 5: 動的なツリーノード |
詳細ページの追加
ここでは、次の図に示すように、アプリケーションに 2 ページ目を追加します。このページでは、プロパティーシートコンポーネントを使用して、最初のページでユーザーが選択した旅行の詳細を動的に表示します。
図 6: 詳細ページ |
-
「プロジェクト」ウィンドウを開き、「Web ページ」ノードを右クリックし、ポップアップメニューから「新規」>「Visual Web JSF ページ」を選択します。新しいページに「
Trip 」という名前を付けます。
-
「サービス」ウィンドウを開き、「表」>「TRIP」ノードを、「Trip」ページのビジュアルデザイナ上にドラッグします。
「ナビゲータ」ウィンドウの「Trip」セクションに「tripDataProvider」ノードが表示され、「SessionBean1」セクションに「tripRowSet1」ノードが表示されます。
- 「ナビゲータ」ウィンドウで、「tripRowSet1」ノードを右クリックし、「SQL 文を編集」を選択します。
クエリーエディタのデザイングリッドで、TRIPID 行の任意のセルを右クリックし、「クエリー条件を追加」を選択します。ダイアログで、「比較」ドロップダウンリストを「=等しい」に設定し、「パラメータ」ラジオボタンを選択します。「閉じる」をクリックします。
TRIPID の「条件」列に「=?」と表示され、SQL クエリーに次の WHERE 句が追加されます。 WHERE TRAVEL.TRIP.TRIPID = ?
- ビジュアルデザイナで「Trip」ページを開きます。「パレット」の「基本」セクションからページに「ハイパーリンク」コンポーネントをドラッグし、「
ホーム」と入力して、Enter キーを押します。
ハイパーリンクコンポーネントの「プロパティー」ウィンドウで、「action 」プロパティーの省略符号ボタン (
) をクリックし、ドロップダウンリストから「hyperlink1_action」を選択して、「了解」をクリックします。
IDE によって Java ソースに hyperlink1_action イベントハンドラが追加されます。
- 「パレット」からページに「メッセージグループ」コンポーネントをドラッグし、ハイパーリンクコンポーネントの右側に配置します。
-
「パレット」の「レイアウト」セクションからページに「プロパティーシート」コンポーネントをドラッグし、ハイパーリンクコンポーネントの下に配置します。
プロパティーシートコンポーネントは、旅行情報を配置するためのコンテナです。プロパティーシートコンポーネントにはプロパティーシートセクションが含まれ、その中にさらにプロパティーコンポーネントが含まれます。
「プロパティーシートセクション 1」を選択します。「プロパティー」ウィンドウで、「label」プロパティーを「旅行の詳細」に設定します。
注: プロジェクトのソースレベルが 1.4 に設定されている場合は、「プロパティー」ウィンドウでプロパティーシートのラベルを変更してもラベルは更新されません。
-
「アウトライン」ウィンドウで、「propertySheet1」>「section1」を展開し、「property1」ノードを選択します。「プロパティー」ウィンドウで、「
label」プロパティーを「出発日:」に設定し、Enter キーを押します。
-
「アウトライン」ウィンドウで、「section1」を選択して右クリックして、ポップアップメニューから「追加 プロパティー」を選択します。「プロパティー」ウィンドウで、「
label 」プロパティーを「出発地:」に設定し、Enter キーを押します。
「パレット」から「静的テキスト」コンポーネントをドラッグし、「アウトライン」ウィンドウの「property1」ノードの上にドロップします。
静的テキストが「property1」のサブノードになります。この静的テキストはビジュアルデザイナにも表示されます。
静的テキストコンポーネントを右クリックし、ポップアップメニューから「データにバインド」を選択します。必要に応じて、「データプロバイダにバインド」タブをクリックして、このタブを前面に表示します。次の図に示すように、ダイアログの「データフィールド」で「TRIP.DEPDATE」を選択し、「了解」をクリックします。
ビジュアルデザイナ内で静的テキストコンポーネントに現在の日付が表示されます。
図 8: 「データにバインド」ダイアログ |
- property2 に静的テキストコンポーネントを追加します。静的テキストを TRIP.DEPCITY にバインドします。
コードの追加
ここでは、Page1 で格納された tripId を Trip ページで取得し、Trip ページで格納された personId を Page1 で取得できるようにするコードを追加します。
-
Java エディタで「Trip」ページを開き、 prerender メソッドまでスクロールします。Page1 で格納された tripId をこのメソッドで取得できるように、次のコード ( 太字 部分) を追加します。
| コード例 2: Trip ページの prerender メソッド |
public void prerender() {
// 要求パラメータから旅行者の ID を取得
String parmTripId = (String)
getExternalContext().getRequestParameterMap().get("tripId");
if (parmTripId != null) {
Integer tripId = new Integer(parmTripId);
try {
getSessionBean1().getTripRowSet1().setObject(1, tripId);
tripDataProvider.refresh();
} catch (Exception e) {
error("Cannot display trip " + tripId);
log("Cannot display trip " + tripId, e);
}
}else {
error("No trip id specified.");
}
}
|
setObject メソッドは、旅行のクエリーの最初の引数を tripId に設定します。つまり、クエリー内の「?」を tripId に置き換えます。このクエリーには引数が 1 つしかないので、 setObject を一度だけ呼び出します。tripDataProvider1.refresh() は、CachedRowSet.release() を呼び出して、CachedRowSetDataProvider のカーソルをリセットします。この時点では CachesRowSet を実行しません。
hyperlink1_action メソッドまでスクロールします。personId を Page1 に渡す次のコード ( 太字 部分) を追加します。
| コード例 3: Trip ページの hyperlink1_action メソッド |
public String hyperlink1_action() {
getRequestBean1().setPersonId(
(Integer)tripDataProvider.getValue("trip.personid"));
return null;
}
|
ページナビゲーションの定義
最後に、Page1 のツリーノードから Trip ページへのナビゲーションを指定します。
-
ビジュアルデザイナの「デザイン」ビュー内の任意の場所を右クリックし、「ページナビゲーション」を選択します。
-
Page1.jsp アイコン上にあるコネクタポートをクリックし、Trip.jsp アイコンまでコネクタをドラッグします。
-
Trip.jsp アイコンを展開し、ハイパーリンクコンポーネントから Page1.jsp アイコンまでコネクタをドラッグします。ページナビゲーションの設定は次の図のようになります。
図 9 : ページナビゲーション |
アプリケーションを実行します。ホームページで、旅行者の名前を展開し、旅行の出発日をクリックします。
次の図に示すように、旅行の詳細を示す Trip ページが表示されます。

図 10: 実行時の詳細ページ |
- Trip ページで「ホーム」リンクをクリックします。ホームページでは、最後に選択した旅行の第 1 レベルノードが展開されたままです。
- ツリーのほかの第 1 レベルノードを展開または縮小したり、旅行の出発日をクリックしたりして、アプリケーションの動作を確認します。
その他の可能な操作: ツリーノードへのアクションメソッドのバインド
JavaServer Faces 1.2 のツリーコンポーネントを使用している場合は (プロジェクトのJava EE バージョンの設定が「Java EE 5」プラットフォームになっている場合)、ツリーノードにアクションメソッドをバインドできます。アクションメソッドでは、ツリーコンポーネントの getSelected() メソッドを呼び出して、クリックされたノードを判別できます。次に、その手順を示します。
- 要求 Bean に Integer 型の「tripId」プロパティーを追加します。
Page1 の Java ソースに次のメソッドを追加します。
| コード例 4: Page1 の tripNode_action メソッド |
public String tripNode_action() {
// 在選択されているツリーノードの ID を取得
String nodeId = displayTree.getSelected();
// 指定された ID を持つツリーノードコンポーネントを検索
TreeNode selectedNode =
(TreeNode) this.getForm1().findComponentById(nodeId);
try {
// ノードの id プロパティーは「trip」と tripId で構成される
// tripId を抽出し、次のページ用に保存
Integer tripId = Integer.valueOf(selectedNode.getId().substring(4));
getRequestBean1().setTripId(tripId);
} catch (Exception e) {
error("Can't convert node id to Integer: " +
selectedNode.getId().substring(4));
return null;
}
return "case1";
}
|
- Page1 の
prerender メソッドで、次のコードをコード例 5 のコードに置き換えます。 tripNode.setUrl("/faces/Trip.jsp?tripId=" +
tripDataProvider.getValue("TRIP.TRIPID").toString());
| コード例 5: prerender メソッドの調整 |
ExpressionFactory exFactory =
getApplication().getExpressionFactory();
ELContext elContext =
getFacesContext().getELContext();
tripNode.setActionExpression(
exFactory.createMethodExpression(
elContext, "#{Page1.tripNode_action}",
String.class, new Class<?>[0])); |
- インポートを修正するには、Alt-Shift-F キーを押します。
-
Trip ページの prerender() メソッドの本体を次のコードに置き換えます。
| コード例 6: Trip ページの prerender メソッド |
public void prerender() {
Integer tripId = getRequestBean1().getTripId();
try {
getSessionBean1().getTripRowSet1().setObject(1, tripId);
tripDataProvider1.refresh();
} catch (Exception e) {
error("Cannot display trip " + tripId);
log("Cannot display trip " + tripId, e);
}
}
|
- アプリケーションを実行します。
ツリーノードの選択に関する注意事項
プロジェクトが J2EE 1.4 の場合は、ツリーノードの選択について次の点に注意してください。
- JavaServer Faces 1.1 のツリーコンポーネントでは、選択されたノードを判別するために
getSelected メソッドまたは getCookieSelectedTreeNode メソッドを使用できません。ユーザーがブラウザの Cookie を無効にしている場合、これらのメソッドから正しい値が返されません。また、Cookie を有効にしている場合でも、ユーザーがはじめてページにアクセスし、ノードをクリックしたときに、Cookie から間違った値が返される可能性があります。以前にアクセスしたときの Cookie が残っている場合は、以前に選択された値が返される可能性があります。JavaServer Faces 1.2 バージョンのツリーコンポーネントでは、選択された値が Cookie に保存されないので、この問題は発生しません。
- ツリーノードの強調表示はセッションが変わっても解除されません。このチュートリアルのプログラムを複数回実行すると、最後のセッションで選択したノードが、新しいセッションではじめてページを開いたときにも強調表示されます。この問題は、選択されたノード ID を渡すのに Cookie を使用していることが原因で起こります。
まとめ
このチュートリアルでは、データベース内のデータからツリー構造を構築しました。2 ページ構成のアプリケーションを作成し、最初のページにツリーコンポーネントを配置しました。ツリーの第 1 レベルノードにはデータベースから取得した旅行者の名前を表示し、第 2 レベルノードにはその旅行者の旅行情報を表示しました。最初のページに表示される各旅行を、旅行の詳細を示す 2 ページ目にリンクしました。
関連項目
更新日: 2007 年 12 月 3日