通过数据库数据构建树
此教程演示如何通过数据库中的数据动态地构建树结构。使用 NetBeans IDE 6.5,您将构建一个双页面应用程序,第一个页面将包含 JSF 1.2 (Woodstock) “树”组件。使用数据库中的姓名填充“树”组件中的一级节点,使用此人的行程填充其二级节点。行程节点链接到第二个页面,用于显示行程的详细信息。
预计时间:30 分钟
目录

要学习本教程,您需要具备以下软件和资源。
* 要利用 NetBeans IDE 的 Java EE 5 功能,请使用完全符合 Java EE 5 规范的应用服务器,例如 GlassFish Application Server V2 UR2。如果使用的是其他服务器,请查阅发行说明和常见问题解答,了解已知问题和解决方法。有关支持的服务器和 Java EE 平台的详细信息,请参见发行说明。
首先,构建包含“树”组件和行程数据库表的 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 。
结束语
在此教程中,您通过数据库中的数据构建了一个树结构。您构建了一个双页面应用程序,其中第一个页面包含了“树”组件。使用数据库中的姓名填充“树”中的一级节点,并使用此人的行程填充二级节点。链接第一个页面的每个行程到第二个页面,此页面显示了该行程的详细信息。
另请参见
本页的最后修改时间:2008 年 10 月 22 日