mb_language("uni"); mb_internal_encoding("utf-8"); mb_http_input("auto"); mb_http_output("utf-8"); ?>
前回は、住所録アプリケーションのアイテム一覧画面と、アイテム詳細画面の作成を紹介しました。実際に動いている画面を見て、Xprioriを利用したアプリケーションだからといって、特に難しいことをしているわけではないことを理解していただけたのではないでしょうか。
今回は、前回予告したようにアイテム一覧画面にページング機能と、検索機能を追加します。前回の記事にも掲載しましたが、今回作成する画面は以下のような機能をもっています。
● アイテム一覧画面
1. 登録したアイテムの番号、名前の一覧を表示(前回)
2. 選択したアイテムの詳細ページに遷移(前回)
3. 20件ごとにページ遷移(今回)
4. 名前で検索(今回)
5. 新規アイテム登録ページに遷移(次回)
前回のアイテム一覧画面は、1つのページに全てのアイテムを表示していたので、アイテム数が増えれば増えるほどページが長くなり、スクロールが必要な見にくいページ構成でした。そこで、1ページあたりの表示アイテム数を20件までとし、それ以上のアイテムを見たい場合は次のページに進むという機能を追加することにします。(この機能をページング機能と呼びます)なお、ページ番号はリクエストパラメータで指定することで、該当ページだけを表示できるようにします。
ページング機能を実装するにあたって、ページ管理を行うためのクラス(PagerBean)を用意します。PagerBeanは現在のページ番号や、次/前ページがあるかないかの判断などを行うクラスです。メンバ変数としては、下記の値を持っており、それぞれの取得/設定ができます。
変数名 |
説明 |
初期値 |
offset | 1ページあたりの表示件数 | 20 |
page_num | 現在のページ番号 | 1 |
next_page | 次ページ番号(次ページがない場合は0) | 0 |
prev_page | 前ページ番号(前ページがない場合は0) | 0 |
max_item | 一覧で表示する全件数 | 0 |
● 次/前ページの取得(getNext_page/getPrev_page)
offset、page_num、max_itemから次/前ページ番号を計算し、次/前ページ番号があればその番号を、なければ0を返します。
public int getNext_page(){ // 現ページの件数が全件数以上であれば次ページは無い if(this.offset * this.page_num >= this.max_item){ this.next_page = 0; } else{ this.next_page = this.page_num + 1; } return this.next_page; } |
public int getPrev_page(){ // 現ページ-1が0以下であれば前ページは無い if(this.page_num - 1 <= 0){ this.prev_page = 0; } else{ this.prev_page = this.page_num - 1; } return this.prev_page; } |
一覧画面に検索フォームを用意し、アイテムのname要素に対して入力されたキーワードでの部分検索ができるようにします。前回作成したSearchAddressbookBeanクラスに下記の関数を追加します。
● 指定した文字列をname要素に含むアイテムの件数を取得する(CountItemByName)
前回作成したCountAllItem関数は、登録したitem要素の全件数を取得する関数でしたが、CountItemByName関数は文字列による部分検索を行い、該当する件数を取得する関数です。Xprioriに問い合わせるXPathは、
count(/ND/address_book/item[contains(name,{文字列})]) |
となります。なおXPathで使用しているcontains()は、第一引数で渡した要素名に、第二引数で渡した文字列が含まれている場合は真を、そうで無い場合は偽を返すXPathの関数です。
public int CountItemByName(String word) { // アイテム総数を取得するXPath String xpath = "count(/ND/address_book/item[contains(name,\"" + word + "\")])"; // 検索結果を取得する String tmpItem_xml = xcon.query(xpath); // XMLを解析する Document doc = AddressbookUtils.loadXMLFromString(tmpItem_xml); return AddressbookUtils.getQResultInteger(doc); } |
内部で利用しているAddressbookUtilsクラスのgetQResultInteger関数は、検索結果として返ってくるXML文字列からQuery-Results要素の値を取得し、その値から改行コードを除き、整数値として返す関数です。
● 指定した文字列をname要素に含むitem要素を範囲指定で取得する(getItemsByName)
前回作成したgetItems関数は、登録したitem要素を範囲指定のみで取得する関数でしたが、getItemsByName関数は、文字列による部分検索を行った上で指定範囲のitem要素を取得する関数です。
Xprioriに問い合わせるXPathは、前回の範囲指定条件の前にcontains()を使った文字列検索を含む
/ND/address_book/item[contains(name,{文字列})][position() >= {取得開始番号} and position() < ( {取得開始番号} + {オフセット値} )] |
となります。
public ItemBean[] getItemsByName(String word,int start, int offset) { // 引数からXPathの条件をつくる // 名前にwordを含むitemを取得するための条件 String xpathCondition1 = "contains(name,\"" + word + "\")"; // 指定した範囲のitemを取得するための条件 String xpathCondition2 = "position() >= " + start + " and " + "position() < " + (start + offset); // XPathを問い合わせ検索結果を取得する String itemsXML = xcon.query("/ND/address_book/item[" + xpathCondition1 + "][" + xpathCondition2 + "]"); // 検索結果XMLからDOM Documentを作成する Document doc = AddressbookUtils.loadXMLFromString(itemsXML); NodeList itemnodeList = doc.getElementsByTagName("item"); // オフセット分のItemBean配列を定義する ItemBean[] items = new ItemBean[itemnodeList.getLength()]; for (int i = 0; i < itemnodeList.getLength(); i++) { items[i] = new ItemBean(itemnodeList.item(i)); } return items; } |
今回の実装から、ページ番号と検索キーワードをページ間で渡すためのリクエストパラメータを考慮しなければなりません。ページ番号はページ管理を行うPagerBeanクラス内で取得しますが、検索キーワードはParamBeanというパラメータ処理を行うクラスで別途処理します。ParamBeanクラスは、検索キーワードの取得/設定、さらにURLエンコードした検索キーワードを取得する関数(getEncSword)を持っています。
では、これらのクラスを利用して検索・ページング機能を持ったJSPを書いてみます。
● アイテム一覧画面(itemlist.jsp)
使用するクラスはSearchAddressbookBean、ItemBean、ParamBeanです。コードを以下に示します。
<%@ page language="java" contentType="text/html; charset=Shift_JIS" pageEncoding="Shift_JIS"%> <%-- インポートする --%> <%@page import="addressbook.ItemBean"%> <% request.setCharacterEncoding("Shift_JIS"); %> <jsp:useBean id="searchxpri" class="addressbook.SearchAddressbookBean"></jsp:useBean> <jsp:useBean id="pager" class="addressbook.PagerBean"></jsp:useBean> <jsp:useBean id="param" class="addressbook.ParamBean"></jsp:useBean> <%-- (1)パラメータを取得する --%> <jsp:setProperty name="pager" property="page_num" param="page" /> <jsp:setProperty name="param" property="sword" param="sword" /> <% //(2)検索キーワードをname要素に含むアイテムの数を取得し、表示するアイテムの開始番号を決定する int item_count = searchxpri.CountItemByName(param.getSword()); pager.setMax_item(item_count); int start_item = (pager.getOffset() * (pager.getPrev_page()) ) + 1 ; //(3)該当するアイテムを範囲指定で取得し、表示するアイテムの終了番号を決定する ItemBean[] itemlist = searchxpri.getItemsByName(param.getSword(),start_item,pager.getOffset()); int item_num = itemlist.length; int end_item = start_item + item_num - 1; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS"> <title>アイテム一覧</title> </head> <body> <center> <table width="400"><tr><td> <table border="0" cellpadding="0" cellspacing="0" width="100%"> <tr><td ><h3>登録アイテム一覧</h3></td></tr> </table> <%--(4)検索フォーム--%> <form method="get"> <table border="0" cellspacing="0" cellpadding="3"> <tr><td>名前: <input type="text" name="sword" value="<%= param.getSword() %>"> <input type="submit" value="検索"> </td></tr> </table> </form> <table width="100%" border="0" cellspacing="0" cellpadding="3"> <tr> <td width="1%" nowrap ><font size="4" color="#000fff"><b><%= item_count %></b></font> 件中 <%= start_item %>~ <%= end_item %>件を表示 </td> <td width="1%" nowrap > </td> <%--(5)ページング処理--%> <td width="99%" align="right" > < <% if(pager.getPrev_page() == 0){ %>前ページ<%}else{ %> <a href="/address_book/itemlist.jsp?page=<%= pager.getPrev_page() %> &sword=<%= param.getEncSword() %>" class="content">前ページ</a><% } %> | <% if(pager.getNext_page() == 0){ %>次ページ <%}else{%> <a href="/address_book/itemlist.jsp?page=<%= pager.getNext_page() %> &sword=<%= param.getEncSword() %>" class="content">次ページ</a><% } %> > </td> </tr> </table> <table width="100%" border="1" cellspacing="0" cellpadding="3"> <tr> <th width="100">ID</th><th width="250">名前</th><th width="50">詳細</th> </tr> <%--(6)for文によるループでアイテム情報を表示する--%> <% for(int i = 0; i < item_num;i++){ %> <tr> <td width="100" align="center"><%= itemlist[i].getID() %></td> <td width="250" align="center"><%= itemlist[i].getName() %></td> <td width="50" align="center"> <%-- 詳細画面に検索キーワードを渡す --%> <a href="/address_book/itemdetail.jsp?id=<%= itemlist[i].getID() %>&sword= <%= param.getEncSword() %>">詳細</a></td> <% }%> </tr></table> </td></tr></table> </center> </body> </html> |
処理の流れを説明します。
(1)パラメータに渡されたページ番号と検索キーワードをJSPのsetPropertyタグを使用してPagerBeanとParamBeanに設定する。
(2)検索キーワードを含むアイテム数を、SearchAddressbookBeanクラスのCountItemByName関数を利用して取得し、1ページに表示するアイテムの開始番号を
オフセット値×前ページ番号+1
で計算する。
(3)開始番号とオフセット値で指定した範囲のアイテムを取得し、1ページに表示するアイテムの終了番号を
開始番号+取得したアイテム数-1
で計算する。
(4)検索フォームを作成し、ユーザが検索キーワードを入力できるようにする。すでに入力済みの場合は、入力されたキーワードをテキストボックスに表示する。ここで入力したキーワードは、GET方式でitemlist.jspに送信される。
(5)PagerBeanを利用して、次/前ページ判定を行い、次/前ページへのリンクをつける。
(6)取得した配列をfor文でループし、IDと名前、詳細ページへのリンクを表示する。詳細ページへのリンクには、詳細ページから戻ってきたときに検索キーワードを引き継ぐために、URLエンコードした検索キーワードをパラメータとして渡す。
実行結果は以下のようになります。
▲アイテム一覧画面
● アイテム詳細画面(itemdetail.jsp)
今回機能を追加したページは主にアイテム一覧画面ですが、詳細画面の「一覧に戻る」でページ遷移したときにキーワードによる検索内容がリセットされないように、検索キーワードを保持する機能を追加しています。ParamBeanクラスを利用して一覧画面からキーワードを受け取り、「一覧に戻る」のリンクにはURLエンコードしたキーワードを渡します。詳細なコードはitemdetail.jspをご覧ください。
前回と今回で、アイテム一覧画面とアイテム詳細画面を作成しました。Xprioriからデータを取得し表示するまでの流れを理解していただけたのではないでしょうか。XprioriなどのXML DBでは、RDBでSQLを使って検索を行うのと同じように、XPathやXQueryを使って検索を行うことができます。XPathにはcount関数やcontain関数などの便利な関数が豊富に用意されており、それらの関数を利用することで、またXQueryを使うことでより複雑な検索が可能です。使うDBの種類は異なりますが、DBに問い合わせを行い、結果を画面に表示するという基本的な考え方は共通ですので、RDBからXML DBに変わったからといって、急に敷居が高くなるわけではありません。次回はアイテム登録画面の実装を通して、Xprioriにデータを登録する流れを紹介します。
上記のjspを実装した場合、Tomcatサーバーの設定によっては文字化けが発生することがあります。そのときはtomcatの設定を下記の手順で変更してください。
(1)第2回でEclipse上に作成したサーバーのserver.xmlを開きます。
▲server.xmlの選択
(2)開いたserver.xmlのport属性値が8080のConnectorタグを選択し、右クリックします。次に[属性の追加]→[useBodyEncodingForURI]を選びます。
▲useBodyEncodingForURI属性の追加
(3)追加したuseBodyEncodingForURIの値をtrue入力し、server.xmlの設定変更を完了します。
▲useBodyEncodingForURI属性の値を設定
今回作成したjavaクラス・jspファイルはこちらからダウンロードすることができます。
・ jspファイルはWebアプリケーションフォルダのaddress_book/WebContentに配置してください。
・ javaファイルはWebアプリケーションフォルダのaddress_book/WebContent/WEB-INF/srcに配置してください。
また、ご自分でクラスを新規作成する場合は、Eclipseのメニューバーより、[ファイル]→[新規]→[クラス]を選択し、[次へ]を押してください。
次の画面で[ソースフォルダ]に「address_book/src」、[パッケージ]に「addressbook」を入力し、[名前]に適切なクラス名を入力し、[終了]を押すことで、新規のクラスを作成することができます。
▲このページのTOPへ