QA@IT

DAOからDBへアクセスする方法について

14208 PV

DAOによるDBへのアクセス方法についての質問です。

開発環境は、

OS:Windows7
ブラウザ:IE
eclipce:3.6
Tomcat:7.0
DB:Oracle11g・SQLPlus

となります。

添付画像の一覧画面にて表示されている情報は、DBに登録してあるものとなっています。
ここにて存在してある検索テキストボックスにて「あいまい検索を行いたい」というのが今回の流れです。

例えば、「ク」と入力すれば、ダークソウルとジュラシック・パーク、この2つが表示されるように、
前後での検索一致を盛り込みたいと考えています。

画面はjspで作成しています。

①検索テキストボックスにて値を受け取ります。「ク」と入力され、
検索ボタンが押されたとします。

servletの一部分です。

// 一覧/検索画面の検索を押した際の処理  
}else if("serch".equals(serch)){  

    // 「ク」を受け取ります 
    String serchWord = request.getParameter("serchWord");  

    // ②受け取った「ク」と接続オブジェクトを引数に渡します。DAOに飛びます 
    List<BookBean>list_ = bd.selectOrSerch(con_,serchWord);  

    // ⑤ DAOにて「ク」が含まれるDB内の全情報を取得 
    request.setAttribute("BOOK_LIST",list_);  

    //遷移先を一覧/検索画面にする  
    address = LIST_ADDRESS; 

③DAOでDBに格納してあるデータを取り出します。select処理です。

/**  
* ジャンル名による絞り込みの結果を取得するメソッド  
* 現在はあいまい検索による結果を取得するメソッド  
* @param con 接続オブジェクト  
* @return genreSerch クライアントの全入力情報  
**/  
public List<BookBean> selectOrSerch(Connection con,String genreSerch) throws Exception{  

    List<BookBean> list = new ArrayList<BookBean>();  

    // 現在は使用していないインスタンスです 
    CharOperator co = new CharOperator();  

    try{  

        //ジャンル検索で全てを選択または一覧検索画面に遷移した場合  
        if("all".equals(genreSerch)){  

            ps_ = con.prepareStatement(SELECTBOOK_QUERY);  



        }  

        /**  
         * 上の記述は関係ありません。 
         * あいまい検索を行った場合  
         */  
        else{  

            /** 
             * プリペアーステートメントにSQL文をセット。SERCHNAME_QUERYの内容はは次に記述します 
             * SERCHNAME_QUERYはstaticインポートにより直接使用しています 
             */ 
            ps_ = con.prepareStatement(SERCHNAME_QUERY);  

            // 「ク」をバインドします 
            ps_.setString(1,genreSerch); // ⑥

        }  

        // SQL文の発行 
        rs_ = ps_.executeQuery();  

        // レコードの最終行になるまで 
        while(rs_.next()){  

            BookBean bb = new BookBean();  

            //レコード1件分のデータをBeanに格納する  
            bb.setId(rs_.getInt("ID"));  
            bb.setGenre(rs_.getString("GENRE"));  
            bb.setName(rs_.getString("BOOK"));  
            bb.setReleaseDay(rs_.getString("SALE_DATE"));  
            bb.setPopularity(rs_.getInt("FAVORITE"));  
            bb.setStock(rs_.getString("STOCK"));  
            bb.setComment(rs_.getString("COM"));  

            //Beanに格納した1件のレコードをListへ追加する  
            list.add(bb);  

        }  

    }catch(Exception e){  
        throw e;  

    }finally{  

        try{  

            // クローズ処理 
            if(rs_ != null)    rs_.close();  
            if(ps_ != null)    ps_.close();  

        }catch (SQLException e){  
             throw e;  
        }  

    }  

    //全入力情報を返す ④servletに戻ります 
    return list;  

} 

以上が全体的なざっくりとした流れになります。検索はSQLがDBに登録してあるデータに対して行うので、 肝はSERCHNAME_QUERYの部分だと思うのです。以降が質問内容となります。

あいまい検索というものがよく分かっていなかったので、一度SQLPlus内で

select name from item(テーブル名です) where name like '%ク%' order by id;と実行すると、

ダークソウル
ジュラシック・パーク

と表示されたので、これを元にSQL文を作成してみました

package servlet;  

/**  
* サーブレットで用いる定数クラス  
**/  

public interface Constants {  

    /**  
     * あいまい検索を行うSQL文  
     */  
    public static final String SERCHNAME_QUERY = "SELECT NAME FROM ITEM WHERE NAME LIKE ? ORDER BY ID"

} 

DAO内の⑥の記述をps_.setString(1,"%" + genreSerch + "%");

としてみたのですが、「行検索が無効」というエラーが出てしまいます。
情報が入っているDBに対しての検索なのでこれでいいのでは?と思っていたのですが、
あいまい検索を掛けるテーブルはITEMですが、もう1つGENREというテーブルがあり、この2つを
紐付けた上で全体の情報をSELECT処理により表示していました。

CREATE TABLE GENRE( 
ID    NUMBER(2), 
NAME    VARCHAR(20) NOT NULL, 
Primary Key(ID) 
); 

CREATE TABLE ITEM( 
ID    NUMBER(5), 
GENRE_ID    NUMBER(2), 
NAME    VARCHAR(50) NOT NULL, 
SALE_DATE    DATE NOT NULL, 
FAVORITE    NUMBER(1) NOT NULL, 
STORK    CHAR(1) NOT NULL, 
COM    VARCHAR(200), 
Primary Key(ID), 
FOREIGN Key(GENRE_ID) REFERENCES GENRE(ID) 
); 

テーブルITEMの外部キーがテーブルGENREのIDとなります。

最初は既に情報が登録されているテーブルITEMのNAMEにのみ検索を掛けて、条件にあったものを持って来ればよい、と 考えていましたが、ジャンルを登録した後の、全ての情報取得は以下のSQLで実行していました。

//書籍情報をselect処理により取得するSQL文 
public static final String SELECTBOOK_QUERY = 

"SELECT TO_NUMBER(ni.ID) ID,ng.NAME GENRE,ni.NAME BOOK,TO_CHAR(ni.SALE_DATE,'yyyy/mm/dd') SALE_DATE,ni.FAVORITE FAVORITE,ni.STOCK STOCK,ni.COM COM" + 
"FROM ITEM ni,GENRE ng WHERE ni.GENRE_ID = ng.ID ORDER BY ni.ID "; 

両方のテーブルを結合し、登録されているジャンルとテーブルITEMの内容を、テーブルITEMのID順に並べるという処理内容です。

なので、上記のSQLのように「ジャンルが登録されているテーブルとジャンルを含めた全情報が登録されているテーブルを紐付けた後に」先ほどのあいまい検索のSQLをどこかで設定してやる必要があるのでは?と考え直しました。これで正解かどうかは分からないのですが。

なので質問としては、上記のSELECT文を元にしたあいまい検索の追加記述の方法を、どなたか教えていただけないでしょうか?どうぞよろしくお願い致します。

回答

前回の質問でも書きましたが質問にコード書くときは </> ボタン使うと便利です。記号を全角にしなくてもよくなります。(そこは直しませんでしたが)

あと、serch ではなく search ですね。
面倒でしょうけど直しておいた方がいいと思いますよ。


回答

ざっと見ただけですが、Oracleであいまい検索でバインド変数使いたい場合は
以下の様にすればよかったように思います。

public static final String SERCHNAME_QUERY = "SELECT NAME FROM ITEM WHERE NAME LIKE '%'||?||'%' ORDER BY ID"

バインドは今のままで構いません。

   ps_.setString(1,genreSerch); // ⑥

|| は文字結合です。のバインド時はなにもせず、OracleのSQL上で 前後の '%' を付けています。

setStringに与える変数(genereSerch)に % を含んでしまう場合、本当の % 文字列として利用されると思います。(そうじゃないと 商品名に半角 % を含むものが絶対に検索できなくなりますよね?)

「行検索が無効」というエラーが出てしまいます。

このエラーはエラーの情報たりないので原因はわからないですが、%を含む文字を検索できないわけないので、%を含んでいてもバインドはできるし検索はできるはずだと思います。
なので 単に検索結果が 0件だと同じエラーが出たりしませんか?(検索そのものではなくて結果 0件の時のその後の処理で落ちている。 rs が 結果 0件時は無効になるなど。0件かどうかチェックするメソッドがありそうな気がします。)

編集 履歴 (1)
  • 回答誠にありがとう御座います。つまり、私が最終的に行き着いた「情報全てを取得するSELECT文を基にする」は関係なく、単純にあいまい検索の方法自体を間違えていた為にエラーが発生していた?という解釈でいいのでしょうか。 -
  • 「あいまい検索の方法自体を間違えていた」をどうとらえているかわかりませんが、「情報全てを取得するSELECT文を基にする」ないし「実際に動くSQLを元にする」という考え方は間違っていません。そこから先はパラメータの使用方法の問題となります。 -
  • 「あいまい検索の方法自体を間違えていた為にエラーが発生していた?」に関してはそうは言っていません。あいまい検索抜きにして、「検索結果が0件だとエラーが起きたりしませんか?」と聞いています。(深くまでみてないので根拠はないですが) -
ウォッチ

この質問への回答やコメントをメールでお知らせします。