2012年9月26日 星期三

存貨管理系統 - MySQL (Part IV):新增


第二個範例 (Part IV):新增

The following examples had been tested on Mozilla's Firefox and Microsoft's IE. The document is provided as is. You are welcomed to use it for non-commercial purpose.
Written by: 國立中興大學資管系呂瑞麟 Eric Jui-Lin Lu

請勿轉貼
看其他教材

新增存貨資料

顯示完所有的待辦事項後,需要完成的就剩下視窗下半部的新增、修改、以及 刪除。一般來說,這三個步驟習慣上會先實作新增;新增的資料顯示後,我們 緊接著實作修改;等到修改後的資料也正確了,最後就實作刪除,把剛剛新增的 資料除去;這樣的順序可以確保所有的功能都被完成,而且不會留下不必要的 測試資料。 資料的新增、修改、以及刪除在程式設計上需要考量的部分比較多,讓我們拿 之前 Part II 的顯示來說明。由於存貨資料不但儲存在資料庫中,為了能夠讓資料能夠 在畫面上正確的處理,程式裡面還有兩個地方包含有存貨資料,一個是 allItems,另一個是在 id 為 box 的 <listbox> 內(嚴格來說,是在其子元素 <listitem> 內);也就是說,每一次資料庫內的資料改變,其相對應的 allItems 以及 box 都需要跟著改變。
有了以上的概念,我們先說明資料的新增處理;新增存貨資料的步驟如下:使用者 在輸入欄位內輸入資料之後,點選"新增"按鈕;在按下"新增"按鈕之後, 資料庫、allItems、以及 box 的資料隨之增加,最後,將輸入欄位內的資料 清除。
點選"新增"按鈕的處理機制: 在使用者按下"新增"按鈕之後,這會觸發一個事件,因此我們需要定義一個 該事件的處理方法,在本範例中稱之為 add()。add() 方法需要完成的事情包含 從輸入欄位取得資料,然後把資料新增到資料庫,最後重新更新畫面。首先, 我們先註冊事件,必須更改新增的按鈕標籤如下:
<button label="新增" width="36px" height="24px" onClick="add()"/>
其中,綠色的部分就是告訴 ZK:如果使用者點選了"新增"按鈕,請執行 add() 方法。跟其他 Java 程式相同,請在 <zscript> 標籤內加入 add() 方法如下:
  void add() {
  }
在 add() 方法中的第一件事情就是從輸入欄位取得資料。請參考 Part I 中的 四個輸入欄位,分別是 num、name、price、和 qty;ZK 提供了一個 非常方便的方式(非常類似 Javascript)來取得這些欄位的值,那就是在這些 名稱的後面直接加上 .value;取得了輸入欄位的值之後,我們就可以初始化 一個新的 Product 物件如下,並將該物件加到 allItems 內:
    Product newp = new Product(num.value.intValue(), name.value, 
                               price.value.doubleValue(), qty.value.intValue());
    allItems.add(newp);
由於 Product 物件的第一個資料成員是 id,其資料型態為 int,可是在利用 .value 的方式取得該輸入欄位的值,其資料型態會依據該輸入欄位的 型態而回傳適當的資料型態;由於料號的輸入欄位型態為 intbox,因此 num.value 的資料型態為 java.lang.Integer,所以我們需要藉由 intValue() 的用法,把它改成 int;同樣的,第四個資料成員 qty, 也需要同樣的轉換; Product 物件的第三個資料成員是 double,而其相對應的輸入欄位型態為 decimalbox,利用 price.value 會回傳 java.Math.BigDecimal 資料型態,因此我們需要使用 doubleValue() 將資料 轉換成 double;至於 name,由於其資料型態為字串,且其輸入欄位型態為 textbox(回傳 java.lang.String),因此不需要轉換。 (請注意,intValue() 和 doubleValue() 是 ZK 提供的功能)。 將新增的 Product 物件加到 allItems 內之後,我們可以將輸入資料加到資料庫中, 其方式如下:
    try {
      Class.forName("com.mysql.jdbc.Driver");
      conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1/eric", "jlu", "newpasswd");
      stmt = conn.createStatement();
      String iSQL = "insert into Product values(" + num.value + ",'" +                           
                     name.value + "'," + price.value + "," + qty.value + ")";
      if (stmt.executeUpdate(iSQL) &lt;= 0)
        throw new SQLException("資料新增失敗");
    } catch (SQLException e) {
      e.printStackTrace();
    } finally {
      try {
        stmt.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
      try {
        conn.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
新增資料的程式碼跟以前介紹的 Java 程式碼幾乎相同,因此我們不多做介紹。 但是這一類的程式如果在 ZK 的環境下執行,有一個地方要特別注意:所有在 Java 程式中,如果出現 <(小於)或者 >(大於)的符號,是不能直接 使用 < 或者 >,而必須分別使用 &lt; 或者 &gt; 來替代;以範例中的程式碼為例,我們使用一個 if 敘述來判斷 stmt.executeUpdate(iSQL) 是否成功;也就是說資料是否成功的新增 到資料庫中,stmt.executeUpdate(iSQL) 會回傳 1,如果有 1 筆資料被影響; 因此,我們判斷 stmt.executeUpdate(iSQL) 小於等於零時,就代表新增失敗; 所以,在程式中的 if 敘述,我們寫的是 stmt.executeUpdate(iSQL) &lt;=0,而不是 stmt.executeUpdate(iSQL) <=0。 到這個步驟,新增加的代辦事項已經加到資料庫以及 allItems,剩下的工作就是 畫面的更新(也就是把資料加到 box 內)。 畫面的更新分成兩個部分:一個是將新增的資料顯示在清單中;另一個就是 把輸入欄位裡面的資料清除掉。新增資料到清單的工作非常類似 DOM 的新增 節點,其程式碼如下:
01      Listitem li = new Listitem(); 
02      li.setValue(newp);  // 在之後的 update, delete 會用到
03      li.appendChild(new Listcell(num.value.toString())); 
04      li.appendChild(new Listcell(name.value)); 
05      li.appendChild(new Listcell(price.value.toString()));
06      li.appendChild(new Listcell(qty.value.toString()));
07      box.appendChild(li);
在第 01 行中,我們新增了一個 Listitem 的物件;Listitem 物件代表 <listbox> 標籤中的一個 <lisitem> 標籤。在第 03 到 06 行,我們需要為 Listitem 物件新增四個子節點,而每一個節點都是一個 Listcell 物件;也就是說, new Listcell(name.value) 會產生 <listcell>name.value</listcell> 標籤,而 name.value 代表 name 欄位內的值。除了 name 之外,其他三個欄位 的回傳資料型態都不是字串,因此其他的回傳值都需要再經過 toString() 將資料轉換成字串。最後,我們在第 07 行把 Listitem 物件(內含四個 Listcell 子節點)加到 box(box 是 <listbox> 的 id 值)下,並成為它的子節點。新增完了之後,ZK 會自動 refresh 畫面。 在上述程式碼中,如果目的只有呈現新的資料,那麼第 02 行的程式碼是不需要的。 第 02 行的目的在於為 Listitem 物件的 value 屬性(還記得之前介紹的 forEach 嗎?)加上一個 newp 的屬性值,這個 屬性值對目前的工作是沒有必要的,而它加入的目的在於讓之後的 update 和 delete 用的。
最後,就是把欄位資料清空,清空的方式非常簡單,直接把欄位值設定為 null 即可;例如,把 name 欄位的資料清空的方式就是 name.value = null;; 其他三個欄位也比照辦理即可。執行整個程式碼,並新增資料後的畫面如下:

add() 的完整程式碼如下:
  void add() {
    // 確保新增的資料也在 allItems 內
    Product newp = new Product(num.value.intValue(), name.value, 
                               price.value.doubleValue(), qty.value.intValue());
    allItems.add(newp);

    // 經資料新增至資料庫
    try {
      Class.forName("com.mysql.jdbc.Driver");
      conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1/eric", "jlu", "newpasswd");
      stmt = conn.createStatement();
      String iSQL = "insert into Product values(" + num.value + ",'" +                           
                     name.value + "'," + price.value + "," + qty.value + ")";
      if (stmt.executeUpdate(iSQL) &lt;= 0)
        throw new SQLException("資料新增失敗");
    } catch (SQLException e) {
      e.printStackTrace();
    } finally {
      try {
        stmt.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
      try {
        conn.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }

    // 將新增資料形成一個 Listitem 物件(或者節點) 
    Listitem li = new Listitem(); 
    li.setValue(newp);  // 在之後的 update, delete 會用到
    li.appendChild(new Listcell(num.value.toString())); 
    li.appendChild(new Listcell(name.value)); 
    li.appendChild(new Listcell(price.value.toString())); 
    li.appendChild(new Listcell(qty.value.toString())); 

    // 將 Listitem 物件變成 box 的子節點
    // box 是 listbox 的 id 值
    box.appendChild(li);

    // 清除輸入欄位
    num.value = null;
    name.value = null;

    // 嗯,在 XP + Tomcat 6.x + ZK 5.0.x 的組合下,price.value 會出現
    // NullPointerException;需要改成 price.setText("");
    // 但是,Win7 x64 + Tomcat 5.5.x + ZK 5.0.x 的組合下,
    // price.null 卻 OK。
    //price.value = null;
    price.setText("");
    qty.value = null;
  }
請注意:雖然這個程式到目前看起來一切 OK,實際上它的設計是有瑕疵的, 例如,請問如果 allItems 新增了之後,但是資料庫的新增卻由於網路斷線 而無法新增,請問這該怎麼辦? 練習題: 請重新設計範例程式,使得只有在資料庫正確的更新的之後, allItems 和 box 才能更新。

Written by: 國立中興大學資管系呂瑞麟 Eric Jui-Lin Lu











沒有留言:

張貼留言