>> 更多 Visual Web Pack 文档
生成基于数据库数据的树结构
撰稿人:Chris Kutler、Gail Chappell 和 James Branam。
| 2007 年 5 月 [修订号:V5.5.1-1] |
|
|
本教程向您说明了如何以动态方式生成一个基于数据库数据的树结构。您将使用 NetBeans Visual Web Pack 5.5 来构建一个包含两个页面的应用程序,其中第一页含有树组件,在树结构中,您将使用数据库中的人名来填充第一级节点,然后使用旅游者的行程来填充第二级节点。行程节点将与显示其行程详细信息的第二页相链接。
|
|
目录
|
|
 |
本教程将使用以下技术及资源
JavaServer Faces 组件/ Java EE 平台 |
1.2/Java EE 5* 1.1/J2EE 1.4
|
| Travel 数据库 |
必需 |
| BluePrints AJAX 组件库 |
不是必需的 |
* 该教程发布时,只有 Sun Java System Application Server 支持 Java EE 5。
本教程是针对使用 Sun Java Application Server PE 9.0 Update Release 1 和 Tomcat 5.5.17 进行定制的。如果您使用的是其他服务器,请参考发行说明和常见问题解答以了解已知问题和解决方法。有关支持的服务器和 Java EE 平台的详细信息,请参见发行说明。
设计主页
首先,将生成一个包含树组件和 TRIP 数据库表的主页,下图显示了该主页。
 图 1:页面设计 |
-
创建一个新的 Visual Web 应用程序项目,并将其命名为
DatabaseTree。
-
将一个“树”组件从组件面板的“基本”类别拖动到页面上,键入 Travel Information,然后按 Enter 键。在“属性”窗口中,将其 id 属性设置为 displayTree,并将 clientSide 属性设置为 True。
当 clientSide 设置为 True 时,每个子节点(无论展开或折叠)都将被发送到客户端,但只有在其父节点展开时才可见。当 clientSide 设置为 False 时,仅呈现那些父节点处于展开状态的子节点。
-
选择“树节点 1”,然后单击鼠标右键,从弹出式菜单中选择“删除”。
对于本应用程序,不需要 IDE 为其创建的初始树节点,这是因为您将使用编程方式来填充树结构。如果不删除该节点,由于系统会优先采用 JSP 标记属性中设置的值,而不是运行时中设置的值,则页面将显示此节点。
-
将一个“消息组”组件从组件面板拖动到页面的边缘位置,如页面的右上角。
连接数据库
接下来,将该页与 Travel 数据源中的数据库表相连接,然后使用查询编辑器修改用于检索数据的 SQL 查询,以便按字母顺序来显示旅游者的姓名,以及按时间顺序显示旅游日期。
-
在“运行环境”窗口中,展开“数据库”节点并验证是否已连接了 TRAVEL 数据库。
如果 TRAVEL 数据库的 jdbc 节点标记显示为连接中断,并且无法展开该节点,则表明 IDE 未连接该数据库。要连接 TRAVEL 数据库,请右键单击 TRAVEL 数据库的 jdbc 节点,然后从弹出式菜单中选择“连接”。如果出现“连接”对话框,请输入 travel 作为口令,选中“在此会话期间记住口令”,然后单击“确定”。如果未看见 TRAVEL 数据库的 jdbc 节点,请参见 NetBeans Visual Web Pack 安装说明以了解如何为 IDE 提供可用的数据库。
注意:如果您使用的是 Apache Tomcat,请将 derbyClient.jar 文件复制到 <tomcat_install>/common/lib 目录,然后再尝试连接至该数据库。
-
展开 TRAVEL 数据库的 jdbc 节点,然后展开“表”节点,如下图所示。
 图 2:Travel 数据库 |
将 "TRIP" 节点拖放到可视设计器上。
此时在“概要”窗口的 "Page1" 部分中将显示 "tripDataProvider" 节点,在 "SessionBean1" 部分中将显示 "tripRowSet" 节点。
在“概要”窗口中,右键单击 "tripRowSet" 节点,然后选择“编辑 SQL 语句”。
查询编辑器将在编辑区域显示 TRIP 表图。
-
在“运行环境”窗口中,将 "TRAVEL" >“表”下面的 "PERSON" 节点拖放在查询编辑器中的 TRIP 表图的旁边,如图 3 所示。
将出现另一个表图,并在两个表图之间建立链接。
- 在 PERSON 表中,清除 PERSONID 的复选框标记。
-
在查询编辑器的“设计网格”中,找到 TRAVEL.PERSON 表的 NAME 行。单击“排序类型”单元格,然后从下拉列表中选择“升序”。
此操作根据姓氏将数据库表中的名字按字母顺序排列。
-
找到 TRAVEL.TRIP 表的 DEPDATE 行。单击“排序类型”单元格,然后从下拉列表中选择“升序”。
此操作将日期按照从旧到新的顺序排列。下图显示了该查询编辑器。
 图 3:查询编辑器 |
生成基于数据库表的树结构
现在,您需要添加一个请求 Bean 属性来存储可供应用程序的两个页面使用的信息。然后将代码添加到 prerender() 方法中,以动态方式生成基于 TRIP 和 PERSON 数据库表的树组件。
-
打开 Page1,以便可以看到“概要”窗口。在“概要”窗口中,右键单击 "RequestBean1" 节点,然后选择“添加”>“属性”。将该属性命名为 personId,并输入 Integer 作为类型,然后单击“确定”。
此属性将用于存储旅游者的 ID,如下图所示。稍后,将构建一个行程详细信息页面,该页面将使用此属性将当前旅游者的 ID 传递给 Page1。设置此属性后,Page1 将可以展开该旅游者的节点。
图 4:添加新属性 |
注意: 由于此版本的产品存在缺陷,因此可能需要在您右键单击 RequestBean 节点时关闭弹出式菜单,然后重新右键单击 RequestBean 节点来激活“添加”子菜单。
-
在 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 排序的行程记录。对于每个 personId,代码会在树结构中创建一个新节点(第一级节点)。然后,为每一个与该 personId 关联的行程创建第二级节点(嵌套节点)。最后,将第二级行程节点与 tripNode_action 方法(稍后在本部分中创建)绑定在一起。
- 按 Alt-Shift-F 组合键以修复无法找到类造成的错误。在“修复导入”对话框中,请确保 java.util.List 出现在“全限定名称”字段中,然后单击“确定”。
运行项目。
将会打开 Web 浏览器,并显示一个树组件,其中每个第一级节点显示人名,如下图所示。展开某个节点会显示此人的旅游日期。请注意,人名按旅游者姓氏的字母顺序显示,并且旅游日期按时间顺序显示,如下图所示。在下一部分中,您将添加一些代码,以便用户在单击行程节点时导航至第二页。第二页显示了用户所选行程的详细信息。
图 5:动态的树节点 |
添加详细信息页
在本部分中,您需要为应用程序添加第二页,如下图所示。此页使用属性表单组件以动态方式显示用户在第一页上选择的行程的详细信息。
图 6:详细信息页 |
-
打开“项目”窗口,右键单击“Web 页”节点,然后从弹出式菜单中选择“新建”>“页”。将新页命名为
Trip。
-
打开“运行环境”窗口,然后将“表”下的 "TRIP" 节点拖放到 Trip 页的可视设计器上。在该对话框中,选择“创建 SessionBean1 tripRowSet1”旁边的单选按钮,如下图所示。单击“确定”。
图 7 :添加具有行集的新数据提供器 |
“概要”窗口将在 "Trip" 部分中显示 "tripDataProvider1" 节点,在 "SessionBean1" 部分中显示 "tripRowSet1" 节点。
- 在“概要”窗口中,右键单击 "tripRowSet1" 节点,然后选择“编辑 SQL 语句”。
在查询编辑器的“设计网格”中,右键单击 TRIPID 行中的任意单元格,然后选择“添加查询条件”。在对话框中,从“比较”下拉列表中选择“= 等于”,并选中“参数”单选按钮。单击“确定”。
将在 TRIPID 的“条件”列中看到 "= ?",并且在 SQL 查询中添加以下 WHERE 子句:WHERE TRAVEL.TRIP.TRIPID = ?
- 在可视设计器中打开 Trip 页。将一个“超级链接”组件从组件面板的“基本”类别拖动到页面上,键入
Home,然后按 Enter 键。
在超级链接组件的“属性”窗口中,单击 action 属性旁边的省略号 (...) 按钮,从下拉列表中选择 hyperlink1_action,然后单击“确定”。
IDE 将为 Java 源代码添加 hyperlink1_action 事件处理程序。
- 将一个“消息组”组件从组件面板拖动到页面上,然后将其放在超级链接组件的右侧。
-
将一个“属性表单”组件从组件面板的“布局”类别拖动到页面上,将其放在超级链接组件的下方。
属性表单组件提供了一个设置行程信息布局的容器。属性表单组件包含一个“属性表单部分”,而该部分又包含一个“属性”组件。
选择“属性表单部分 1”。在“属性”窗口中,将 label 属性设置为 Trip Details。
注意:如果项目源代码级别的设置为 1.4,则在“属性”窗口中更改了 label 属性后,属性表单的标签将不会发生更新。
-
在“概要”窗口中,展开 "propertySheet1" > "section1",然后选择 "property1" 节点。在“属性”窗口中,将
label 属性设置为 Departure Date:,然后按 Enter 键。
-
在“概要”窗口中,选择 "section1",然后单击鼠标右键,从弹出式菜单中选择“添加属性”。在“属性”窗口中,将
label 属性设置为 Departure City:,然后按 Enter 键。
将一个“静态文本”组件从组件面板拖放到“概要”窗口中的 "property1" 节点上。
该静态文本将成为 property1 的一个子节点。同时该文本会出现在可视设计器中。
右键单击“静态文本”组件,然后从弹出式菜单中选择“绑定到数据”。如有必要,请单击“绑定到数据提供器”标签将其置于前端。在该对话框中,在“数据字段”中选择 TRIP.DEPDATE(如下图所示),然后单击“确定”。
当前日期会出现在可视设计器的静态文本组件中。
图 8:“绑定到数据”对话框 |
- 将静态文本组件添加到 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。也就是说,该方法将查询中的 ? 替换为 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 图标。下图显示了页面导航设置。
图 9:页面导航 |
运行应用程序。在主页上,展开旅游者的姓名,然后单击某个行程日期。
将打开 Trip 页,其中包含了该行程的详细信息,如下图所示。
图 10:运行时的详细信息页 |
- 在 Trip 页上,单击 "Home" 链接。请注意,您在主页上最后选择的行程,其第一级节点仍处于展开状态。
- 通过展开或折叠第一级树节点并单击行程日期可以继续浏览该应用程序。
执行更多操作:将操作方法与树节点绑定在一起
如果您使用的是 JavaServer Faces 1.2 树组件(即,项目的 Java EE 版本设置为 Java EE 5 平台),则可以将操作方法与树节点绑定在一起;并且可以在操作方法中调用树组件的 getSelected() 方法来确定单击了哪个节点,如以下步骤所示。
- 将 Integer 类型的 tripId 属性添加到请求 Bean 中。
将以下方法添加到 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";
}
|
- 在 page1 的
prerender 方法中,将以下代码替换为代码样例 5 中的代码。 tripNode.setUrl("/faces/Trip.jsp?tripId=" +
tripDataProvider.getValue("TRIP.TRIPID").toString());
| 代码样例 5:调整 prerender 方法 |
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 方法来确定您选中了哪个节点。如果用户禁用了其浏览器中的 cookie,则这些方法将不会返回正确的值。同时,如果浏览器中的 cookie 处于启用状态时,在用户第一次访问页面并且单击某个节点时 cookie 可能会返回错误的值。如果保留了上次访问所遗留的 cookie 信息,则会返回上次访问所选定的值。由于 JavaServer Faces 1.2 版本的树组件不使用 cookie 来保存选定的值,因此对于 1.2 版本来说不存在此问题。
- 在一个会话阶段中出现的树节点的突出显示状态不会在另一个会话阶段中消失。如果您在本教程中多次运行了该程序,则上次会话选定的节点在新会话中第一次打开该页面时仍处于突出显示状态。此问题是由于您使用 cookie 传输选定的节点 ID 造成的。
摘要
在本教程中,您构建了一个基于数据库数据的树结构。您构建了一个包含两个页面的应用程序,其中第一页含有树组件。您使用数据库中的人名填充树的第一级节点,并且使用旅游者的行程填充第二级节点。并且还将第一页上的每个行程与第二页链接在一起,其中第二页显示行程的详细信息。
另请参见:
此页的最新修改时间:2007 年 5 月 24 日