了解应用程序

既然您的项目已包含了实体类、用于控制实体类的 EJB 会话 Bean 和 JSF 驱动的用于显示和修改数据库的前端,那么请尝试运行该项目以查看结果。

以下是一系列可选的小练习,可用来进一步熟悉应用程序,以及 IDE 所提供的功能。

检查已完成的项目

  1. 要运行此项目,可以在“项目”窗口中右键单击项目节点并选择“运行”,也可以单击主工具栏上的“运行项目”(“运行项目”按钮) 按钮。 

    当显示应用程序的欢迎页面时,将提供一个链接列表,用于查看包含在每个数据库表中的条目。 
    浏览器中的 ConsultingAgency 欢迎页面 
    当完成了“通过实体类创建 JSF 页”向导时,链接将被添加到欢迎页面 (index.xhtml)。这些链接将作为 Facelets 页(为“咨询机构”数据库提供 CRUD 功能)的入口点。
    <h:body>
        Hello from Facelets
        <h:form>
            <h:commandLink action="/address/List" value="Show All Address Items"/>
        </h:form>
        <h:form>
            <h:commandLink action="/billable/List" value="Show All Billable Items"/>
        </h:form>
        <h:form>
            <h:commandLink action="/client/List" value="Show All Client Items"/>
        </h:form>
        <h:form>
            <h:commandLink action="/consultant/List" value="Show All Consultant Items"/>
        </h:form>
        <h:form>
            <h:commandLink action="/consultantStatus/List" value="Show All ConsultantStatus Items"/>
        </h:form>
        <h:form>
            <h:commandLink action="/project/List" value="Show All Project Items"/>
        </h:form>
        <h:form>
            <h:commandLink action="/recruiter/List" value="Show All Recruiter Items"/>
        </h:form>
    </h:body>
  2. 单击 "Show All Consultant Items" 链接。查看上面的代码,则可以看到目标页为 /consultant/List.xhtml。(在 JSF 2.0 中,由于隐式导航所以文件扩展名是推断出来的。) 
    "Consultants" 页 
    数据库目前不包含任何样例数据。可以通过单击 "Create New Consultant" 链接和使用提供的 Web 表单手动添加数据。此操作将触发/consultant/Create.xhtml 页进行显示。还在 IDE 中运行 SQL 脚本以使用样例数据填充表。以下子部分对这两个选项进行了说明。

您可以单击索引链接返回到欢迎页面列出的链接。这些链接提供了保存在每个数据库表中数据的视图,并触发了每个实体文件夹中 List.xhtml 文件的显示。稍后会有说明,在将数据添加到表之后,会显示其他链接,其中每个条目都允许您查看 (View.xhtml)、编辑 (Edit.xhmtl) 和销毁单个表记录的数据。

使用 SQL 脚本来填充数据库

运行提供的脚本,该脚本会生成数据库表的样例数据。脚本 (mysql_insert_data_consult.sql) 包含在咨询机构数据库 zip 文件中,该文件可从所需软件表中下载。

根据所使用的数据库服务器(MySQL 或 JavaDB),您可以运行提供的脚本,该脚本会为数据库表生成样例数据。对于 MySQL,该脚本为mysql_insert_data_consult.sql。对于 JavaDB,该脚本为 javadb_insert_data_consult.sql。两个脚本分别包含在各自的归档文件中,可以通过所需软件表进行下载。

  1. 从主菜单中选择“文件”>“打开文件”,然后导航到脚本在您的计算机上的位置。单击“打开”。文件会自动在 IDE 的 SQL 编辑器中打开。
  2. 确保在 SQL 编辑器工具栏的“连接”下拉列表中选择了 consult 数据库。 
    SQL 编辑器和插入数据脚本的屏幕快照 
    可以在编辑器中右键单击并选择“运行语句”,也可以单击“运行 SQL”(“运行 SQL”按钮) 按钮。您可以在“输出”窗口中查看脚本运行的结果。
  3. 重新启动 GlassFish 服务器。这是使服务器可以重新装入和缓存 consult 数据库中包含的新数据所必需的步骤。为此,请单击“输出”窗口中的 GlassFish 服务器标签(GlassFish 服务器标签显示服务器日志),然后单击左旁注中的“重新启动服务器”(“重新启动服务器”按钮) 按钮。GlassFish 服务器即会停止,然后重新启动。
  4. 重新运行项目,然后单击 "Show All Consultant Items" 链接。将看到该列表不再为空。 


了解 Facelets 页中的编辑器支持

  1. 在编辑器中打开/consultant/List.xhtml页。第 8 行表明该页的呈现依赖于 Facelets template.xhtml文件。
    <ui:composition template="/template.xhtml">

    要显示行号,请右键单击编辑器的左旁注,然后选择“显示行号”。

  2. 使用 IDE 的“转至文件”对话框来打开 template.xhtml。按 Alt-Shift-O 组合键(在 Mac 上为 Ctrl-Shift-O 组合键),然后开始键入 template。 
    “转至文件”对话框 
    单击“确定”(或按 Enter 键)。
  3. 模板将使用 <ui:insert> 标记将其他文件的内容插入到其标题和正文中。将光标置于 <ui:insert> 标记上,然后按 Ctrl-空格键调用文档弹出式窗口。 
    显示在编辑器中的文档弹出式窗口 
    可以在 JSF 标记及其属性上按 Ctrl-空格键调用文档弹出式窗口。您查看的文档是从正式的 JSF 标记库文档所提供的描述中获取的。
  4. 切换回 List.xhtml 文件(按 Ctrl-Tab 组合键)。<ui:define> 标记用来定义将应用于模板标题和正文的内容。为每个实体类生成的所有 4 个 Facelets 文件(Create.xhtmlEdit.xhtmlList.xhtml 和 View.xhtml)都使用了此模式。
  5. 将您的光标置于任何 EL 表达式(用于包含在 Bundle.properties 文件中的本地化消息)上。按 Ctrl-空格键查看本地化消息。 

    在上图上,可以看到 EL 表达式解析为 "List",该表达式将应用到模板标题,并且可以从呈现在浏览器中的页面得到验证。
  6. 滚动到文件底部并找到 Create New Consultant 链接的代码(第 92 行)。如下所示:
    <h:commandLink action="#{consultantController.prepareCreate}" value="#{bundle.ListConsultantCreateLink}"/>
  7. 在 commandLink 的 action 属性上按 Ctrl-空格键以调用文档弹出式窗口。 

    action 属性表示在浏览器中单击链接时处理请求的方法。将提供以下文档: 

    方法表达式,用于表示在用户激活此组件时要调用的应用程序操作。该表达式的值必须为一个没有参数且返回对象(调用其 toString() 以派生逻辑结果)的公共方法,该对象将被传递到此应用程序的 NavigationHandler。

    换句话说,action 值通常是指 JSF 受管 Bean 中的一个方法,其值为 String。然后 JSF 的 NavigationHandler 会使用该字符串将请求转发到相应的视图中。可通过以下步骤来验证这一点。
  8. 将光标置于 consultantController 上,然后按 Ctrl-空格键。编辑器的代码完成表明 consultantController 是一个 JSF 受管 Bean。 
    在编辑器中调用的代码完成
  9. 将光标移动到 prepareCreate,然后按 Ctrl-空格键。代码完成列出了包含在 ConsultantController 受管 Bean 中的方法。 
    在编辑器中调用的代码完成
  10. 按 Ctrl 键(在 Mac 上为 &#8984 键),然后将鼠标悬停在 prepareCreate 上。形成了一个链接,您可以直接导航至 ConsultantController 受管 Bean 中的prepareCreate() 方法。 
    显示在编辑器中的链接
  11. 单击链接并查看 prepareCreate() 方法(如下所示)。
    public String prepareCreate() {
        current = new Consultant();
        selectedItemIndex = -1;
        return "Create";
    }
    该方法将返回 CreateNavigationHandler 在后台收集信息,并将 Create 字符串应用于以视图(为响应请求而发送)为目标的路径:/consultant/Create.xhtml。(在 JSF 2.0 中,由于隐式导航所以文件扩展名是推断出来的。)

通过字段验证来了解数据库完整性

  1. 在浏览器的 Consultants List 页中,单击 "Create New Consultant" 链接。正如之前子部分中的说明,此操作将触发呈现 /consultant/Create.xhtml 页。
  2. 在表单中输入以下详细信息。目前,将 RecruiterId 和 StatusId 字段保留为空。 

    字段
    ConsultantId2
    Emailjack.smart@jsfcrudconsultants.com
    Passwordjack.smart
    HourlyRate75
    BillableHourlyRate110
    HireDate07/22/2008
    ResumeI'm a great consultant.Hire me - You won't be disappointed!
    RecruiterId---
    StatusId---
  3. 单击“保存”。当您执行此操作后,将对 StatusId 字段标记一个验证错误。 
    包含样例数据的 "Create New Consultant" 页 
    为什么会出现这种情况?重新检查“咨询机构”数据库的实体关系图。如上面的关系表中所述,CONSULTANT 和 CONSULTANT_STATUS 表共享一个不可为 null 的一对多的关系。所以,CONSULTANT 表中的每一个条目都必须包含对 CONSULTANT_STATUS 表中条目的引用。这将通过链接这两个表的consultant_fk_consultant_status 外键来表示。

    可以通过在“服务”窗口中展开一个表的“外键”节点(Ctrl-5 组合键;在 Mac 上为 &#8984-5 组合键)来查看表中保存的外键。

    “服务”窗口 - 顾问表的外键
  4. 要解决验证错误,请从 StatusId 下拉列表中选择 entity.ConsultantStatus[statusId=A]。 

    注意:RecruiterId 字段可保留为空。正如数据库实体关系图中所示,在 CONSULTANT 和 RECRUITER 表之间存在一个可为 null 的一对多的关系,这就意味着CONSULTANT 中的条目不需要与一个 RECRUITER 条目相关联。
  5. 单击“保存”。将显示一条消息,表明顾问条目已成功保存。如果单击 Show All Consultant Items,将看到新条目列在表中。

通常,生成的 Facelets 页会为产生下列问题的用户输入提供出错信息:

  • 不可为 null 的表单元格中出现了空字段。
  • 对不可更改的数据(例如主键)进行了修改。
  • 插入数据的类型不正确。
  • 当用户视图与数据库不再同步时对数据进行了修改。

编辑实体类

在前面的子部分中,您看到了 StatusId 下拉列表是如何为您提供不那么容易使用的 entity.ConsultantStatus[statusId=A] 选项。您可能已经注意到:此下拉列表中针对每一项显示的文本都是每个遇到的 ConsultantStatus 实体(即,调用了实体类的 toString() 方法)的字符串表示。

该子部分将演示您可以使用编辑器的代码完成、文档和导航支持作此结论的方式。此外,还教您为下拉列表准备更加易于使用的消息。

  1. 在编辑器中打开 /consultant/Create.xhtml 文件。这是您刚在浏览器中看到的 "Create New Consultant" 表单。向下滚动到 StatusId 下拉列表的代码处(如下面粗体所示)。
        <h:outputLabel value="#{bundle.CreateConsultantLabel_resume}" for="resume" />
        <h:inputTextarea rows="4" cols="30" id="resume" value="#{consultantController.selected.resume}" title="#{bundle.CreateConsultantTitle_resume}" />
        <h:outputLabel value="#{bundle.CreateConsultantLabel_statusId}" for="statusId" />     <h:selectOneMenu id="statusId" value="#{consultantController.selected.statusId}" title="#{bundle.CreateConsultantTitle_statusId}" required="true" requiredMessage="#{bundle.CreateConsultantRequiredMessage_statusId}">         <f:selectItems value="#{consultantStatusController.itemsAvailableSelectOne}"/>     </h:selectOneMenu>
        <h:outputLabel value="#{bundle.CreateConsultantLabel_recruiterId}" for="recruiterId" />
        <h:selectOneMenu id="recruiterId" value="#{consultantController.selected.recruiterId}" title="#{bundle.CreateConsultantTitle_recruiterId}" >
            <f:selectItems value="#{recruiterController.itemsAvailableSelectOne}"/>
        </h:selectOneMenu>
    </h:panelGrid>
  2. 检查应用于 <f:selectItems> 标记的 valuevalue 属性决定了为下拉列表中每项显示的文本。 

    在 itemsAvailableSelectOne 上按 Ctrl-空格键。编辑器的代码完成表示 ConsultantStatusController 的 getItemsAvailableSelectOne() 方法返回了SelectItem 对象的数组。 
    编辑器中调用的代码完成
  3. 按 Ctrl 键(在 Mac 上为 &#8984 键),然后将鼠标悬停在 itemsAvailableSelectOne 上。形成了一个链接,您可以直接导航至 ConsultantStatus 实体源代码中的 getItemsAvailableSelectOne() 方法。单击该链接。
  4. 将光标置于方法签名中的 SelectItem[] 返回值上,然后按 Ctrl-空格键调用文档弹出式窗口。 
    Java 类中调用的文档弹出式窗口

    在文档窗口中单击 Web 浏览器 (Web 浏览器图标) 图标,在外部 Web 浏览器中打开 Javadoc。

    正如您所看到的,SelectItem 类属于 JSF 框架。文档中提到的 UISelectOne 组件使用您在上述步骤 1 中检查的标记中的 <h:selectOneMenu> 标记来表示。
  5. 按 Ctrl 键(在 Mac 上为 &#8984 键),然后将鼠标悬停在 findAll() 上。随即出现一个弹出式窗口,显示方法签名。 
    方法签名的弹出式窗口 
    可以看到此处的 ejbFacade.findAll() 返回了 ConsultantStatus 对象的 List
  6. 导航至 JsfUtil.getSelectItems。将鼠标悬停在 getSelectItems 上并按 Ctrl 键(在 Mac 上为 &#8984),然后单击显示的链接。 

    注意:回想一下,JsfUtil 是在您完成“通过实体类创建 JSF 页”向导时生成的一个实用程序类。 

    该方法对实体列表(即 ConsultantStatus 对象的 List)执行循环操作,为每个实体创建 SelectItem。如下面粗体所示,每个 SelectItem 都是使用实体对象和对象的标签来创建的。
    public static SelectItem[] getSelectItems(List<?> entities, boolean selectOne) {
        int size = selectOne ? entities.size() + 1 : entities.size();
        SelectItem[] items = new SelectItem[size];
        int i = 0;
        if (selectOne) {
            items[0] = new SelectItem("", "---");
            i++;
        }
        for (Object x : entities) {         items[i++] = new SelectItem(x, x.toString());     }
        return items;
    }
    该标签使用实体的 toString() 方法来创建,并且在响应中呈现对象时是对象的表示。(请参见 SelectItem(java.lang.Object value, java.lang.String label) 构造函数的 Javadoc 定义。) 

    既然您已在查看下拉列表中的项时,验证了实体 toString() 方法正是浏览器中所呈现的内容,那么请修改 ConsultantStatus toString() 方法。
  7. 在编辑器中打开 ConsultantStatus 实体类。修改 toString 方法以返回 statusId 和 description。这些是对应于 CONSULTANT_STATUS 表的两列的实体属性。
    public String toString() {
        return statusId + ", " + description;
    }
  8. 重新运行该项目。当浏览器显示欢迎页面时,单击 Show All Consultant Items 链接,然后单击 Create New Consultant。 

    检查 StatusId 下拉列表。将看到现在显示的是数据库的 CONSULTANT_STATUS 表中所包含的一个记录的状态 ID 和描述。 
    StatusId 下拉列表的浏览器显示