2010年11月21日 星期日

JSP 教學 - PreparedStatement 的使用

在一般的 Java 程式裡如果要插入一筆資料到資料表中,最基本的方法都是用 java.sql.Statement 去做,譬如:
Statement state = connection.createStatement();
state.executeUpdate("SQL 語法");
通常上面的 SQL 語法會寫的太凌亂,譬如太多的單引號和雙引號交錯導致可讀性不易,例如


state.executeQuery("INSERT INTO temp(account, user, password) VALUES('" + account + "','" + username + "','" + password + "')"); //以上的三個變數都為字串
以上有個替代方法就是利用 java.sql.PreparedStatement
可以很簡單的表示成以下


String sql = "INSERT INTO temp(account, user, password) VALUES(?, ? ,?)";
PreparedStatement prestate =  connection.prepareStatement(sql); //先建立一個 SQL 語句並回傳一個 PreparedStatement 物件
prestate.setString(1, account); //對應到第一個問號
prestate.setString(2, user); //對應到第二個問號
prestate.setString(3, password); //對應到第三個問號
prestate.executeUpdate();  //真正執行


以上的參數都為字串型態,當然除了字串以外還有其他型態,例如 setInt()...等


而在使用 Statement 物件時,通常最常使用的
executeUpdate、executeQuery()、execute()
在 PreparedStatement 也是正常使用的,值得提醒的是 execute()該方法會回傳布林值


若為 true 代表結果為 ResultSet 物件,通常都是執行查詢。
若為 false 代表結果會回傳一個整數或是沒結果,表示的是更新的資料數。


說到用 PreparedStatement 的好處還有防止 SQL injection 的攻擊


不過如果可以的話其實可以在使用者輸入的資料進行篩選,例如
account.matches("[a-z0-9]+")


如此一來或多或少可以防止 SQL injection


接下來介紹一些更進階的技巧,如果要製作一個專門處理資料庫操作的共用元件


這樣可以減少在 Servlet 或是 JSP 中雜亂的資料庫操作程式碼


但是由於在設定資料參數值的時候可能有多種的型態和多種的操作,如上所述


這時候可以寫一個共通的 method 來執行全部的 PreparedStatement 操作


public boolean exceuteQueUpd(String sql, Object[] params)
{   //sql 為語法, params 的 Object 型態陣列 為所有參數
    if(connection == null){
        if(!getConnection()){
            return false;
        }
    }
    try{
        pstate = connection.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
        for(int i=0;i<params.length;i++){ 
            pstate.setObject(i+1, params[i]);//資料參數的設定是從1開始
        }
        return pstate.execute();  
        //因為有可能會執行任何一種的SQL 語句,所以統一使用 execute
          //而非特定的 executeUpdate() 或是 executeQuery()
    }catch(SQLException sqle){
        System.out.println(sqle);
        return false;
    }
}



以上的片斷程式,會傳入一個 Object 型態的陣列,簡單說該陣列包含了所有?的資料


接著用迴圈將 Object 的每個參數進行設定,用 setObject 方法


以下以一個範例執行以上定一的 exceuteQueUpd()method

db.exceuteQueUpd("SELECT * FROM member WHERE account = ? AND ip = ?", 
new Object[]{new String("ayu780129"), new Integer(ip)});

再舉一個例子


db.exceuteQueUpd("UPDATE member SET ip = ? WHERE account = ?", new Object[]{new Integer(ip), new String("ayu780129")});

以上就如同一般的 PreparedStatement 使用一樣,不同的是不用再依據資料的型態做不同的設定,如 setInt、setString、setDouble 等等。


只要將所有要設定的參數資料一併都放數 Object 陣列即可,就解決的資料型態不固定的問題了


第一個例子如果沒發生例外會回傳 true,代表可以透過 PreparedStatement 的 getResultSet() 取得 ResultSet


第二個例子的話則是會回傳 false,可以透過 PreparedStatement 的 getUpdateCount()取得更新的資料數


所以可以透過執行 exceuteQueUpd() 回傳的布林值判斷,並加以處理


之後再介紹更多 java SQL 的說明

1 則留言:

  1. 大大請教,我用你的方法做INSERT後

    發現資料有錯誤

    但因資料量大無法一一驗證

    如何把INSERT的錯誤SQL字串取出來

    煩請解惑~感恩!

    回覆刪除