FeaturesDocs & SupportCommunityBlogsPartners

通过数据库数据构建树

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

预计时间:30 分钟

目录

此页面内容适用于 NetBeans IDE 6.5

要学习本教程,您需要具备以下软件和资源。

软件或资源 要求的版本
NetBeans IDE Java 6.5 版本
Java 开发工具包 (Java Developer Kit, JDK) 版本 6 或版本 5
JavaServer Faces 组件/
Java EE 平台
1.2(带有 Java EE 5*)或
1.1(带有 J2EE 1.4)
GlassFish 应用服务器 V2
Travel 数据库 必需

* 要利用 NetBeans IDE 的 Java EE 5 功能,请使用完全符合 Java EE 5 规范的应用服务器,例如 GlassFish Application Server V2 UR2。如果使用的是其他服务器,请查阅发行说明常见问题解答,了解已知问题和解决方法。有关支持的服务器和 Java EE 平台的详细信息,请参见发行说明

设计 Home 页面

首先,构建包含“树”组件和行程数据库表的 Home 页面。页面显示如下。

页面设计

  1. 创建一个新的 Visual Web JSF 应用程序项目,将其命名为 DatabaseTree,并启用 Visual Web JavaServer Faces 框架。
  2. 从“组件面板”的“Woodstock 基本”部分拖入一个“树”组件到页面,键入 Travel Information,然后按下 Enter 键。在“属性”窗口中,将 id 属性设置为 displayTree,并将 clientSide 属性设置为 True

    clientSide True 时,每个子节点(展开或未展开)都将被发送到客户端,除非父节点被展开,否则看不到子节点。当 clientSide False时,仅呈现已展开父节点的子节点。
  3. 选择 Tree Node 1 ,单击鼠标右键,然后从弹出式菜单中选择“删除”。

    在此应用程序中,您将通过编程方式填充树,因此不需要初始化 IDE 创建的树节点。如果没有删除节点,在 JSP 标签属性中设置的值将优先于运行时设置的值,并在页面中显示节点。
  4. 右键单击“树”组件并选择“添加绑定属性”
  5. 从“组件面板”拖动一个“消息组”组件放入页面的偏僻位置上,例如页面右上角。

配置数据库

建立数据库

在本节中,您将在 IDE 中建立 travel 数据库和 MySQL 数据库。

  1. 确保您的机器上已安装并运行 MySQL 数据库服务器。有关连接 MySQL 数据库的更多信息,请参见连接到 MySQL 数据库
  2. 在“服务”窗口中,右键单击 “MySQL 服务器”节点并选择“创建数据库”。

    此时将打开“创建新数据库”对话框。

    “创建 MySQL 数据库”对话框
  3. 从“新建数据库名称”下拉列表中选择“样例数据库:travel ”并单击“确定”。

    在“服务”窗口中,travel 数据库将出现在“MySQL 服务器”节点下。

    “服务”窗口中的 VIR 数据库

连接到数据库

下一步,使用 Travel 数据源中数据库表连接页面。然后,使用“查询编辑器”修改用于检索数据的 SQL 查询,以使遍历名称按字母表次序出现和遍历日期按年月日次序出现。

  1. 打开“服务”窗口,展开 Databases 节点并验证已经连接到 Travel 数据库。

    如果 Travel 数据库的标志 jdbc 节点中断并且不能展开节点,则表示 IDE 没有连上数据库。要连接 travel 数据库,右键单击 travel 的 jdbc 节点并从弹出式菜单中选择“连接”。

  2. 展开 Travel 数据库的 jdbc 节点,展开的表节点应如下图所示。

    Travel 数据库的“服务”窗口
  3. trip 节点拖放到“可视设计器”中。

    “导航”窗口将在 Page1 部分中显示的 tripDataProvider 节点并在 SessionBean1 部分中显示 tripRowSet 节点。
  4. 在“导航”窗口中,展开 SessionBean1 节点,右键单击 RowSet 节点,并选择“编辑 SQL 语句”。

    查询编辑器将出现在编辑区域,并带有一个 trip 表格图。
  5. 从“服务”窗口中,将 Travel > Tables > person 节点拖放到 trip 表格图旁,如下图所示。

    第一个图旁将出现另外一个表格图。
  6. person 表中,清除 personid 复选框。
  7. 在“查询编辑器”的“设计网格”中,查找 person 表的 name 行。单击“排序类型”并从下拉列表中选择“升序”。

    此操作将数据库表中的名字按姓的字母表顺序排序。
  8. 查找 trip 表中的 depdate 行。单击“排序类型”单元格,并从下拉列表中选择“升序”。

    此操作将 trip 日期按从最早日期到最晚日期顺序排序。下图显示了查询编辑器。

    查询编辑器

通过数据库表构建树

现在,添加查询 bean 属性,用于存储应用程序中的两个页面所使用的信息。然后,添加代码到 prerender()方法中,通过 TRIP 和 PERSON 数据库表动态地构建“树”组件

  1. 打开 Page1,使“导航”窗口可见。在“导航”窗口中,右键单击 RequestBean1 节点并选择“编辑 Java 源代码”。

  2. 在构造函数 public class RequestBean1 extends AbstractRequestBean 下方,声明以下属性:

    private Integer personId;
  3. 在“Java 编辑器”中,单击鼠标右键并选择“重构”>“封装字段”。
  4. 在“封装字段”对话框中,检查方框以创建 "getter" 和 "setter" 方法,如下图所示。确保变量声明如下:字段的可见性为 "private" 和存取方法的可见性为 "public",然后单击“重构”。
    “重构字段”对话框
  5. 在 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 方法,在本节之后将创建该方法。
  6. 在源代码上单击右键并从弹出式菜单选择“修复导入”以修复类,使其不出现错误。在“修复所有导入”对话框中,要确保 com.sun.Webui.jsf.component.TreeNode 出现在 TreeNode 字段中并且 java.util.List 出现在 List 字段中。单击“确定”。
  7. 运行项目

    Web 浏览器将打开并显示“树”组件,每个一级节点都指明人的姓名,如下图所示。展开节点,显示该人的遍历日期。请注意,姓名是按姓的字母表顺序显示,日期是按年月日顺序显示。在下节中,您将添加代码,让用户单击行程节点时能够导航到第二个页面。第二个页面显示用户所选行程的详细信息。

    动态树节点

添加详细信息页面

现在,您将在应用程序中添加第二个页面,如下图所示。此页面使用“属性表单”组件动态显示用户在第一个页面中选择的行程的详细信息。

详细信息页面

  1. 打开“项目”窗口,右键单击“Web 页”节点并从弹出式菜单中选择“新建”>“Visual Web JSF 页”。将新页面命名为 Trip
  2. 打开“服务”窗口并将“表”>trip节点拖放到可视设计器的 Trip 页面中。

    此时将出现“添加新数据提供器”对话框。
  3. 在“添加新数据提供器”对话框中,在 SessionBean1 中选择“创建 tripRowSet1 ”,如下图所示。单击“确定”。

    添加“新数据提供器”对话框

    “导航”窗口将在 Trip 部分显示 tripDataProvider 节点,并在 SessionBean1 部分显示 tripRowSet1 节点。

  4. 在“导航”窗口中,右键单击 tripRowSet1 节点并选择“编辑 SQL 语句”。
  5. 在“查询编辑器”的“设计网格”中,在 TRIPID 行右键单击任意单元格并选择“添加查询条件”。在对话框中,设置“比较”下拉列表“= 等于”并选中“参数”单选按钮 。单击“确定”。

    您可以 在 TRIPID 的“条件”列看到 =?,其作用是将以下 WHERE 子句添加到 SQL 查询中。
    WHERE trip.tripid = ?
  6. 在“可视设计器”中打开 Trip Page。从“组件面板”的“Woodstock 基本” 部分拖动一个“超级链接”组件到页面上,键入 Home 并按下 Enter 键。
  7. 在“超级连接”组件的“属性”窗口,单击省略号按钮的 action 属性,从下拉列表中选择 hyperlink1_action,然后单击“确定”。

    IDE 将在 Java 源代码中添加 hyperlink1_action 事件处理程序。
  8. 右键单击“超级链接”组件并选择“添加绑定属性”
  9. 从“组件面板”拖动一个“消息组”组件到页面并放到“超级链接”组件的右边。
  10. 从“组件面板”的“Woodstock 布局”部分拖动一个“属性表单”组件放入页面。将其放到“超级链接”组件下面。

    “属性表单”组件将提供容器来排列行程信息。“属性表单”组件包含“属性表单部分”,“属性表单部分”又依次包含“属性”组件。
  11. 选择 Property Sheet Section 1。在“属性”窗口,将 label 属性设置为 Trip Details

    注意:如果项目源代码级别设置为 1.4 ,在“属性”窗口更改它之后属性表单标签不会更新。
  12. 在“导航”窗口中,展开 propertySheet1 > section1 然后选择 property1 节点。在“属性”窗口中,将 label 属性设置为 Departure Date:,然后并按下 Enter 键。
  13. 在“导航”窗口中,选择 section1 ,单击鼠标右键并从弹出式菜单选择“添加属性”。在“属性”窗口,将 label 属性设置为 Departure City:,然后按下 Enter 键。
  14. 在“导航”窗口中,从“组件面板”拖动一个“静态文本”组件放到 property1 节点上。

    “静态文本”组件将成为 property1 的子节点。“静态设计器”也将出现在可视设计器中。
  15. 右键单击“静态文本”组件并选择“添加绑定属性”
  16. 右键单击“静态文本”组件并从下拉式菜单中选择“绑定到数据”。如有必要,单击“绑定到数据提供器”标签使该标签移至顶层。在对话框中,从“数据”字段中选择 trip.depdate ,如下图所示,然后单击“确定”。

    “可视设计器”中的“静态文本”组件将显示当前日期。

    “绑定到数据”对话框
  17. 将“静态文本”组件添加到 property2。将“静态文本”绑定到 trip.depcity
  18. 右键单击“静态文本”组件并选择“添加绑定属性”

添加代码。

在此添加代码,使 Trip 页能获取存储在 Page1 中的 tripid,并且 Page1 能获取存储在 Trip 页中的 personid。

  1. 在 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。
  2. 找到 hyperlink1_action 方法。添加以下代码(显示为粗体),将 personId 传递给 Page1:

    代码样例 3:Trip 页面的 hyperlink1_action 方法
    public String hyperlink1_action() {
    getRequestBean1().setPersonId(
    (Integer)tripDataProvider1.getValue("trip.personid"));

    return null;
    }

定义页面导航

最后,指定从 Page1 上的“树”节点到 Trip 页面的导航。

  1. 在“可视设计器”的“可视”视图中单击鼠标右键并选择“页面导航”。
  2. 单击 Page1.jsp 图标上的连接器端口并将连接器拖到 Trip.jsp 图标上。
  3. 展开 Trip.jsp 图标并将连接器从“超级链接”组件拖到 Page1.jsp 图标上。下图显示了页面导航的设置。

    页面导航
  4. 运行应用程序。在 Home 页面上,展开行程者姓名并单击行程日期。

    Trip 页连同行程的详细信息都将被打开,如下图所示。

    运行时详细信息页面
  5. 在 Trip 页面上,单击 Home 链接。注意,在 Home 页面上,您上次选择的行程一级节点仍然为展开状态。
  6. 展开和折叠一级“树”节点并单击行程日期以继续探讨应用程序。

更多内容:将操作方法绑定到树节点

如果您使用 JavaServer Faces 1.2 的“树”组件(即项目的 Java EE Version 环境是 Java EE 5 平台),则可以绑定操作方法到树节点,同时在操作方法中,调用“树”组件的 getSelected() 方法以决定要选择的节点,见以下步骤说明。

  1. 在整型类型的 Request Bean 中添加 tripId 属性。
  2. 在“源编辑器”中单击右键并选择 “重构” >“封装字段”。
  3. tripId 添加 getter 和 setter 方法,然后单击“确定”。
  4. 在“导航”窗口,展开 Page1 > html1 > body1 ,右键单击 form1 并选择“添加绑定属性”
  5. 将以下方法添加到 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";
    }
  6.  
  7. 在 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]));
  8. 按 Alt-Shift-F 以修复导入。
  9. 在 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);
    }

    }
  10. 运行应用程序。

关于“树”节点选择的注意事项

如果您的项目是 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 日

Companion
Projects:
                  Powered by: