此教程演示如何通過數據庫中的數據動態地構建樹結構。使用 NetBeans IDE 6.5,您將構建一個雙頁面應用程序,第一個頁面將包含 JSF 1.2 (Woodstock) “樹”組件。使用數據庫中的姓名填充“樹”組件中的一級節點,使用此人的行程填充其二級節點。行程節點鏈接到第二個 頁面,用於顯示行程的詳細信息。
預計時間:30 分鐘
目錄
設計 Home 頁面
配置數據庫
建立數據庫
連接到數據庫
通過數據庫表構建樹
添加詳細信息頁面
添加代碼
定義頁面導航
更多內容:將操作方法綁定到樹節點
關於“樹”節點選擇的注意事項
結束語
要學習本教程,您需要具備以下軟件和資源。
* 要利用 NetBeans IDE 的 Java EE 5 功能,請使用完全符合 Java EE 5 規范的應用服務器,例如 GlassFish Server Open Source Edition 2.1 UR2。如果使用的是其他服務器,請查閱發行說明和常見問題解答,了解已知問題和解決方法。有關支持的服務器和 Java EE 平台的詳細信息,請參見發行說明。
設計 Home 頁面
首先,構建包含“樹”組件和行程數據庫表的 Home 頁面。頁面顯示如下。
創建一個新的 Visual Web JSF 應用程序項目,將其命名為 DatabaseTree,並啟用 Visual Web JavaServer Faces 框架。
從“組件面板”的“Woodstock 基本”部分拖入一個“樹”組件到頁面,鍵入 Travel Information,然後按下 Enter 鍵。在“屬性 ”窗口中,將 id 屬性設置為 displayTree,並將 clientSide 屬性設置為 True。
當 clientSide 為 True 時,每個子節點(展開或未展開)都將被發送到客戶端,除非父節點被展開,否則看不到子節點。當 clientSide 為 False時,僅呈現已展開父節點的子節點。
選擇 Tree Node 1 ,單擊鼠標右鍵,然後從彈出式菜單中選擇“刪除”。
在此應用程序中,您將通過編程方式填充樹,因此不需要初始化 IDE 創建的樹節點。如果沒有刪除節點,在 JSP 標簽屬性中設置的值 將優先於運行時設置的值,並在頁面中顯示節點。
右鍵單擊“樹”組件並選擇“添加綁定屬性”。
從“組件面板”拖動一個“消息組”組件放入頁面的偏僻位置上,例如頁面右上角。
配置數據庫
建立數據庫
在本節中,您將在 IDE 中建立 travel 數據庫和 MySQL 數據庫。
確保您的機器上已安裝並運行 MySQL 數據庫服務器。有關連接 MySQL 數據庫的更多信息,請參見連接到 MySQL 數據庫。
在 “服務”窗口中,右鍵單擊 “MySQL 服務器”節點並選擇“創建數據庫”。
此時將打開“創建新數據庫”對話框。
從“新建數據庫名稱”下拉列表中選擇“樣例數據庫:travel ”並單擊“確定”。
在“服務”窗口中,travel 數據庫將出現在“MySQL 服務器”節點下。
連接到數據庫
下一步,使用 Travel 數據源中數據庫表連接頁面。然後,使用“查詢編輯器”修改用於檢索數據的 SQL 查詢,以使遍歷名稱按字 母表次序出現和遍歷日期按年月日次序出現。
打開“服務”窗口,展開 Databases 節點並驗證已經連接到 Travel 數據庫。
如果 Travel 數據庫的標志 jdbc 節點中斷並且不能展開節點,則表示 IDE 沒有連上數據庫。要連接 travel 數據庫,右鍵單擊 travel 的 jdbc 節點並從彈出式菜單中選擇“連接”。
展開 Travel 數據庫的 jdbc 節點,展開的表節點應如下圖所示。
。
將 trip 節點拖放到“可視設計器”中。
“導航”窗口將在 Page1 部分中顯示的 tripDataProvider 節點並在 SessionBean1 部分中顯示 tripRowSet 節點。
在“導航”窗 口中,展開 SessionBean1 節點,右鍵單擊 RowSet 節點,並選擇“編輯 SQL 語句”。
查詢編輯器將出現在編輯區域,並帶有一個 trip 表格圖。
從“服務”窗口中,將 Travel > Tables > person 節點拖放到 trip 表格圖旁,如下圖所示。
第一個圖旁將出現另外一個表格圖。
在 person 表中,清除 personid 復選框。
在“查詢編輯器”的“設計網格”中,查找 person 表的 name 行。單擊“排序類型”並從下拉列表中選擇“升序”。
此操作將數據庫表中的名字按姓的字母表順序排序。
查找 trip 表中的 depdate 行。單擊“排序類型”單元格 ,並從下拉列表中選擇“升序”。
此操作將 trip 日期按從最早日期到最晚日期順序排序。下圖顯示了查詢編輯器。
通過數據庫表構建樹
現在,添加查詢 bean 屬性,用於存儲應用程序中的兩個頁面所使用的信息。然後,添加代碼到 prerender()方法中,通過 TRIP 和 PERSON 數據庫表動態地構建“樹”組件
打開 Page1,使“導航”窗口可見。在“導航”窗口中,右鍵單擊 RequestBean1 節點並選擇“編輯 Java 源代碼”。
在構 造函數 public class RequestBean1 extends AbstractRequestBean 下方,聲明以下屬性:
private Integer personId;
在“Java 編輯器”中,單擊鼠標右鍵並選擇“重構”>“封裝字段”。
在“封裝字段”對話框中,檢查方框以創建 "getter" 和 "setter" 方法,如下圖所示。確保變量聲明如下: 字段的可見性為 "private" 和存取方法的可見性為 "public",然後單擊“重構”。
在 Java 編輯器中,打開 Page1 並找到 prerender 方法。使用下面顯示為粗 體的代碼替換 prerender 方法的主體內容。
樣例代碼 1:Page1 的 prerender 方法
public void prerender() { // If the Request Bean's personId is set, then // we just came back from the Trip page // and had displayed a selected trip. // We use the personId later to determine whether // to expand a person's node Integer expandedPersonId = getRequestBean1().getPersonId(); try { // Set up the variables we will need Integer currentPersonId = new Integer(-1); // If nbrChildren is not 0 then this is a // postback and we have our tree already int nbrChildren = displayTree.getChildCount(); if (nbrChildren == 0) { // List of outer (person) nodes List outerChildren = displayTree.getChildren(); // Erase previous contents outerChildren.clear(); // List of inner (trip) nodes List innerChildren = null; // Execute the SQL query tripDataProvider.refresh(); // Iterate over the rows of the result set. // Every time we encounter a new person, add first level node. // Add second level trip nodes to the parent person node. 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")); // If the request bean passed a person id, // expand that person's node personNode.setExpanded(newPersonId.equals (expandedPersonId)); outerChildren.add(personNode); innerChildren = personNode.getChildren(); } // Create a new trip node 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 排序的 trip 記錄。代碼為每個 personId 在樹中創建新的一級節點。然後,代碼為每個與 personId 相關聯的 trip 創建二級節點(嵌套的節點)。最後,代碼綁定二級 trip 節點到 tripNode_action 方法,在本節之後將創建該方 法。
在源代碼上單擊右鍵並從彈出式菜單選擇“修復導入”以修復類,使其不出現錯誤。在“修復所有導入”對話框中,要確保 com.sun.Webui.jsf.component.TreeNode 出現在 TreeNode 字段中並且 java.util.List 出現在 List 字段中。單擊“確定”。
運行項目
Web 浏覽器將打開並顯示“樹”組件,每個一級節點都指明人的姓名,如下圖所示。展開節點,顯示該人的遍歷日期。請注意,姓名是按姓 的字母表順序顯示,日期是按年月日順序顯示。在下節中,您將添加代碼,讓用戶單擊行程節點時能夠導航到第二個頁面。第二個頁面顯示用 戶所選行程的詳細信息。
添加詳細信息頁面
現在,您將在應用程序中添加第二個頁面,如下圖所示。此頁面使用“屬性表單”組件動態顯示用戶在第一個頁面中選擇的行程的詳 細信息。
打開“項目”窗口,右鍵單擊“Web 頁”節點並從彈出式菜單中選擇“新建”>“Visual Web JSF 頁”。將新頁面命名為 Trip 。
打開“服務”窗口並將“表”>trip節點拖放到可視設計器的 Trip 頁面中。
此時將出現“添加新數據提供器”對話框。
在“添加新數據提供器”對話框中,在 SessionBean1 中選擇“創建 tripRowSet1 ”, 如下圖所示。單擊“確定”。
“導航”窗口將在 Trip 部分顯示 tripDataProvider 節點,並在 SessionBean1 部分顯示 tripRowSet1 節點。
在“導航”窗口中,右鍵單擊 tripRowSet1 節點並選擇“編輯 SQL 語句”。
在“查詢編輯器”的“設計網格”中,在 TRIPID 行右鍵單擊任意單元格並選擇“添加查詢條件”。在對話框中,設置“比較”下拉列表“= 等於”並選中“參數”單選按鈕 。單擊“ 確定”。
您可以 在 TRIPID 的“條件”列看到 =?,其作用是將以下 WHERE 子句添加到 SQL 查詢中。
WHERE trip.tripid = ?
在“可視設計器”中打開 Trip Page。從“組件面板”的“Woodstock 基本” 部分拖動一個“超級鏈接”組件到頁面上,鍵入 Home 並按下 Enter 鍵。
在“超級連接”組件的“屬性”窗口,單擊省略號
按鈕的 action 屬性,從下拉列 表中選擇 hyperlink1_action,然後單擊“確定”。
IDE 將在 Java 源代碼中添加 hyperlink1_action 事件處理程序。
右鍵單擊“超級鏈接”組件並選擇“添加綁定屬性” 。
從“組件面板”拖動一個“消息組”組件到頁面並放到“超級鏈接”組件的右邊。
從“組件面板”的“Woodstock 布局”部 分拖動一個“屬性表單”組件放入頁面。將其放到“超級鏈接”組件下面。
“屬性表單”組件將提供容器來排列行程信息。“屬性表單”組件包含“屬性表單部分”,“屬性表單部分”又依次包含“屬性”組件 。
選擇 Property Sheet Section 1。在“屬性”窗口,將 label 屬性設置為 Trip Details。
注意:如果項目源代碼級別設置為 1.4 ,在“屬性”窗口更改它之後屬性表單標簽不會更新。
在“導航”窗口中,展開 propertySheet1 > section1 然後選擇 property1 節點。在“屬性”窗口中,將 label 屬性設置為 Departure Date:,然後並按下 Enter 鍵。
在“導航”窗口中,選擇 section1 ,單擊鼠標右鍵並從彈出式菜單選擇“添加屬性”。在“屬性”窗口,將 label 屬性設置為 Departure City:,然後按下 Enter 鍵。
在“導航”窗口中,從“組件面板”拖動一個“靜態文本”組件放到 property1 節點上。
“靜態文本”組件將成為 property1 的子節點。“靜態設計器”也將出現在可視設計器中。
右鍵單擊“靜態文本”組件並選擇“添加綁定屬性”。
右鍵單擊“靜態文本”組件並從下拉式菜單中選擇“綁定到數據”。如有必要,單擊“綁定到數據提供器”標簽使該標簽移至頂層。 在對話框中,從“數據”字段中選擇 trip.depdate ,如下圖所示,然後單擊“確定”。
“可視設計器”中的“靜態文本”組件將顯示當前日期。
將“靜態文本”組件添加到 property2。將“靜態文本”綁定到 trip.depcity。
右鍵單擊“靜態文本”組件並選擇“添加綁定屬性”。
添加代碼。
在此添加代碼,使 Trip 頁能獲取存儲在 Page1 中的 tripid,並且 Page1 能獲取存儲在 Trip 頁中的 personid。
在 Java 編輯器中打開 Trip 頁並找到 prerender 方法。添加以下代碼(顯示為粗體),使方法能獲取存儲在 Page1 中的 tripId 。
代碼樣例 2:Trip 頁面的 prerender 方法
public void prerender() { // Get the person id from the request parameters String parmTripId = (String) getExternalContext().getRequestParameterMap().get("tripId"); if (parmTripId != null) { Integer tripId = new Integer(parmTripId); try { getSessionBean1().getTripRowSet1().setObject(1, tripId); tripDataProvider1.refresh(); } catch (Exception e) { error("Cannot display trip " + tripId); log("Cannot display trip " + tripId, e); } }else { error("No trip id specified."); } }
setObject 方法將為 tripId 設置 trip 查詢的第一個變量。 也就是說,該方法將查詢中的 ? 替換為 tripId。查詢僅含有 一個參數,因此僅需調用 setObject 一次。調用 tripDataProvider1.refresh() 時將調用 CachedRowSet.release() 並重置 CachedRowSetDataProvider 的光標。此時並不會執行 CachesRowSet。
找到 hyperlink1_action 方法。添加以下代碼(顯示為粗體),將 personId 傳遞給 Page1:
代碼樣例 3:Trip 頁面的 hyperlink1_action 方法
public String hyperlink1_action() { getRequestBean1().setPersonId( (Integer)tripDataProvider1.getValue("trip.personid")); return null; }
定義頁面導航
最後,指定從 Page1 上的“樹”節點到 Trip 頁面的導航。
在“可視設計器”的“可視”視圖中單擊鼠標右鍵並選擇“頁面導航”。
單擊 Page1.jsp 圖標上的連接器端口並將連接器拖到 Trip.jsp 圖標上。
展開 Trip.jsp 圖標並將連接器從“超級鏈接” 組件拖到 Page1.jsp 圖標上。下圖顯示了頁面導航的設置。
運行應用程序。在 Home 頁面上,展開行程者姓名並單擊行程日期。
Trip 頁連同行程的詳細信息都將被打開,如下圖所示。
在 Trip 頁面上,單擊 Home 鏈接。注意,在 Home 頁面上,您上次選擇的行程一級節點仍然為展開狀態。
展開和折疊一級“樹”節點並單擊行程日期以繼續探討應用程序。
更多內容:將操作方法綁定到樹節點
如果您使用 JavaServer Faces 1.2 的“樹”組件(即項目的 Java EE Version 環境是 Java EE 5 平台),則可以綁定操作方法到 樹節點,同時在操作方法中,調用“樹”組件的 getSelected() 方法以決定要選擇的節點,見以下步驟說明。
在整型類型的 Request Bean 中添加 tripId 屬性。
在“源編輯器”中單擊右鍵並選擇 “重構” >“封裝字段”。
為 tripId 添加 getter 和 setter 方法,然後單擊“確定”。
在“導航”窗口,展開 Page1 > html1 > body1 ,右鍵單擊 form1 並選擇“添加綁定屬性”。
將以下方法添加到 Page1 的 Java 源代碼中。
代碼樣例 4:Page1 的 tripNode_action 方法
public String tripNode_action() { // Get the id of the currently selected tree node String nodeId = displayTree.getSelected(); // Find the tree node component with the given id TreeNode selectedNode = (TreeNode) this.getForm1().findComponentById(nodeId); try { // Node's id property is composed of "trip" plus the trip id // Extract the trip id and save it for the next page 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"; }
在 page 1 的 prerender 方法中,用“代碼樣例 5”中的代碼替換以下代碼。
tripNode.setUrl("/faces/Trip.jsp?tripId=" + tripDataProvider.getValue("TRIP.TRIPID").toString());
Code Sample 5: Tweaking the prerender Method
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 方法來決定節點的選擇。如果用戶關 閉了浏覽器的 cookies,這些方法將不能返回正確值。此外,如果打開了浏覽器的 cookies,當用戶首次訪問頁面並單擊節點時,cookie 也可 能返回錯誤值。如果有從前一個訪問者遺留的 cookies ,可能會返回前一次選擇的值。因為“樹”組件的 JavaServer Faces 1.2 版本不使用 cookies 來保存選擇的值,這對 1.2 版本就不是問題。
在會話期間,沒有清除“樹”節點的高亮顯示功能。在此教程中,如果您不止一次的運行程序,當首次打開頁面時,上次會話選擇的 節點在新的會話中會突出顯示。問題就在於使用 cookies 傳輸所選擇節點的 ID 。
結束語
在此教程中,您通過數據庫中的數據構建了一個樹結構。您構建了一個雙頁面應用程序,其中第一個頁面包含了“樹”組件。使用數據庫中 的姓名填充“樹”中的一級節點,並使用此人的行程填充二級節點。鏈接第一個頁面的每個行程到第二個頁面,此頁面顯示了該行程的詳細信 息。