// 
//     Licensed to the Apache Software Foundation (ASF) under one
//     or more contributor license agreements.  See the NOTICE file
//     distributed with this work for additional information
//     regarding copyright ownership.  The ASF licenses this file
//     to you under the Apache License, Version 2.0 (the
//     "License"); you may not use this file except in compliance
//     with the License.  You may obtain a copy of the License at
// 
//       http://www.apache.org/licenses/LICENSE-2.0
// 
//     Unless required by applicable law or agreed to in writing,
//     software distributed under the License is distributed on an
//     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
//     KIND, either express or implied.  See the License for the
//     specific language governing permissions and limitations
//     under the License.
//

= 在 Web 应用程序中使用 Hibernate
:jbake-type: tutorial
:jbake-tags: tutorials 
:jbake-status: published
:icons: font
:syntax: true
:source-highlighter: pygments
:toc: left
:toc-title:
:description: 在 Web 应用程序中使用 Hibernate - Apache NetBeans
:keywords: Apache NetBeans, Tutorials, 在 Web 应用程序中使用 Hibernate

在本教程中，您将使用 NetBeans IDE 创建和部署显示数据库中数据的 Web 应用程序。Web 应用程序将 Hibernate 框架用作持久性层，以便检索简单的传统 Java 对象 (POJO)，并将其存储到关系数据库中。

Hibernate 是一个为对象关系映射 (ORM) 提供工具的框架。本教程介绍了如何向 IDE 添加对 Hibernate 框架的支持，以及如何创建所需的 Hibernate 文件。在创建 Java 对象并配置应用程序以使用 Hibernate 后，创建一个 JSF 受管 Bean 和 JSF 2.0 页面以显示数据。

在开始本教程之前，您可能需要熟悉以下文档。

* link:http://www.hibernate.org/[+hibernate.org+] 上的 Hibernate 文档
* link:quickstart-webapps.html[+Web 应用程序开发简介+]
* link:jsf20-intro.html[+JavaServer Faces 2.x 简介+]


image::images/netbeans-stamp-74-73-72.png[title="此页上的内容适用于 NetBeans IDE 7.2、7.3 和 7.4"]


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

|===
|软件或资源 |要求的版本 

|link:https://netbeans.org/downloads/index.html[+NetBeans IDE+] |7.1、7.2、7.3、7.4、Java EE 版本 

|link:http://www.oracle.com/technetwork/java/javase/downloads/index.html[+Java 开发工具包 (JDK)+] |版本 6 或 7 

|GlassFish Server Open Source Edition |3.x 或 4.x 

|link:http://www.mysql.com/[+MySQL 数据库服务器+] |版本 5.x 

|Sakila 数据库 |更新中心上可用的插件 
|===

您可以下载link:https://netbeans.org/projects/samples/downloads/download/Samples/JavaEE/DVDStoreEE6.zip[+已完成项目的 zip 档案文件+]。


== 创建数据库

本教程使用一个名为  ``sakila``  的 MySQL 数据库，这是一个免费的 MySQL 数据库，可从 MySQL 网站获得。安装 IDE 时并未包含 sakila 数据库，因此首先需要创建该数据库，以继续学习本教程。

要创建 sakila 数据库，可使用 "Plugins"（插件）管理器下载并安装 "Sakila Sample Database"（Sakila 样例数据库）插件。安装该插件后，sakila 数据库将添加到 "Create MySQL Database"（创建 MySQL 数据库）对话框的数据库列表中。

有关配置 IDE 使用 MySQL 的详细信息，请参见link:../ide/mysql.html[+连接 MySQL 数据库+]教程。

1. 打开 "Plugins"（插件）管理器，并安装 "Sakila Sample Database"（Sakila 样例数据库）插件。
2. 安装插件后，在 "Services"（服务）窗口展开 "Databases"（数据库）节点以启动 MySQL 数据库，右键单击 MySQL 服务器节点并选择 "Start"（启动）。
3. 右键单击 MySQL 服务器节点并选择 "Create Database"（创建数据库）。
4. 在 "Create MySQL Database"（创建 MySQL 数据库）对话框的 "New Database Name"（新数据库名称）下拉列表中选择 "Sakila" 数据库。单击 "OK"（确定）。

image::images/create-sakila-mysql.png[title=""Create MySQL Database"（创建 MySQL 数据库）对话框的屏幕快照"]

单击 "OK"（确定），MySQL 服务器节点下随即出现一个 Sakila 节点。



. 右键单击 "Sakila" 节点，然后选择 "Connect"（连接）。

单击 "Connect"（连接）后，"Databases"（数据库）节点下面将列出 Sakila 数据库连接节点 ( ``jdbc:mysql://localhost:3306/sakila [_username_ on Default]`` )。打开连接时，可通过展开该连接节点查看数据库中数据。


== 创建 Web 应用程序项目

在本练习中，您将创建一个 Web 应用程序项目并为该项目添加 Hibernate 库。创建该项目时，请在新建项目向导的 "Frameworks"（框架）面板中选择 "Hibernate"，并指定数据库。

1. 从主菜单中，选择 "File"（文件）> "New Project"（新建项目）（Ctrl-Shift-N 组合键；在 Mac 上为 ⌘-Shift-N 组合键）。从 "Java Web" 类别中选择 "Web Application"（Web 应用程序），然后单击 "Next"（下一步）。
2. 键入 *DVDStore* 作为项目名称，并设置项目位置。
3. 取消选中 "Use Dedicated Folder"（使用专用文件夹）选项（如果该选项处于选中状态）。单击 "Next"（下一步）。

（对于本教程，没有理由将项目库复制到指定文件夹，因为您将需要与其他用户共享库）。



. 将服务器设置为 "GlassFish Server"，并将 Java EE 版本设置为 "Java EE 6 Web" 或 "Java EE 7 Web"。单击 "Next"（下一步）。


. 选中 "JavaServer Faces" 复选框并使用默认 JSF 2.x 库。


. 在框架列表中选中 "Hibernate" 复选框。


. 从 "Database Connection"（数据库连接）下拉列表中选择 sakila 数据库。单击 "Finish"（完成）。

*注：* 如果向导的 "Frameworks"（框架）面板中没有 sakila 数据库这一选项，请查看该连接是否列在 "Services"（服务）窗口的 "Databases"（数据库）节点下。如果此处没有该连接，则需要创建数据库连接。

image::images/hib-newwebapp.png[title="新建项目向导的 "Frameworks"（框架）面板，其中显示向项目中添加 Hibernate 支持"]

单击 "Finish"（完成），此时 IDE 将创建 Web 应用程序项目，并在编辑器中打开  ``hibernate.cfg.xml``  文件和  ``index.xhtml`` 。

如果展开 "Projects"（项目）窗口中的 "Libraries"（库）节点，则可以看到 IDE 已向项目中添加了 Hibernate 库。

image::images/hib-libraries.png[title="显示 Hibernate 库的 "Projects"（项目）窗口的屏幕快照"] 


== 修改 Hibernate 配置文件

创建使用 Hibernate 框架的新项目时，IDE 会自动在应用程序的上下文类路径的根路径（"Files"（文件）窗口中的  ``src/java`` ）上创建  ``hibernate.cfg.xml``  配置文件。该文件位于 "Projects"（项目）窗口的 "Source Packages"（源包）节点下的  ``<默认包>``  中。该配置文件包含有关数据库连接、资源映射和其他连接属性的信息。您可以使用多视图编辑器编辑该文件或直接在 XML 编辑器中编辑 XML。

在本练习中，您将编辑在  ``hibernate.cfg.xml``  中指定的默认属性，以启用 SQL 语句的调试日志记录以及 Hibernate 的会话上下文管理。

1. 在 "Design"（设计）标签中打开  ``hibernate.cfg.xml`` 。可以通过展开 "Projects"（项目）窗口中 "Source Packages"（源包）下的  ``<default package>``  节点并双击  ``hibernate.cfg.xml``  来打开该文件。
2. 在多视图 XML 编辑器中，展开 "Configuration Properties"（可选属性）下的 "Configuration Properties"（配置属性）节点。
3. 单击 "Add"（添加）以打开 "Add Hibernate Property"（添加 Hibernate 属性）对话框。
4. 在此对话框中，选择  ``hibernate.show_sql``  属性并将值设置为  ``true`` 。这将启用 SQL 语句的调试日志记录。

image::images/add-property-showsql.png[title="显示 hibernate.show_sql 属性的设置值的 "Add Hibernate Property"（添加 Hibernate 属性）对话框"]


. 展开 "Miscellaneous Properties"（其他属性）节点并单击 "Add"（添加）。


. 在此对话框中，选择  ``properties hibernate.current_session_context_class``  并将值设置为  ``thread``  以启用 Hibernate 的自动会话上下文管理。

image::images/add-property-sessioncontext.png[title="显示为 hibernate.current_session_context_class 属性设置值的 "Add Hibernate Property"（添加 Hibernate 属性）对话框"]


. 在 "Miscellaneous Properties"（其他属性）节点下再次单击 "Add"（添加），然后在 "Property Name"（属性名称）下拉列表中选择  ``hibernate.query.factory_class`` 。


. 选择 *org.hibernate.hql.classic.ClassicQueryTranslatorFactory* 作为属性值。单击 "OK"（确定）。

image::images/add-property-factoryclass.png[title="显示 hibernate.query.factory_class 属性的设置值的 "Add Hibernate Property"（添加 Hibernate 属性）对话框"]

如果单击编辑器中的 XML 标签，则可以在 XML 视图中看到此文件。文件应如下所示（三个新属性以粗体显示）：


[source,xml]
----

<hibernate-configuration>
    <session-factory name="session1">
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/sakila</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">######</property>
        *<property name="hibernate.show_sql">true</property>
        <property name="hibernate.current_session_context_class">thread</property>
        <property name="hibernate.query.factory_class">org.hibernate.hql.classic.ClassicQueryTranslatorFactory</property>*
    </session-factory>
</hibernate-configuration>
----


. 保存对该文件所做的更改。

由于不需要再次编辑该文件，因此可以关闭该文件。


== 创建  ``HibernateUtil.java``  帮助文件

要使用 Hibernate，您需要创建一个 helper 类，该类处理启动并访问 Hibernate 的  ``SessionFactory``  以获取 "Session"（会话）对象。该类调用  ``configure()``  并加载  ``hibernate.cfg.xml``  配置文件，然后构建  ``SessionFactory``  以获取 "Session"（会话）对象。

在此部分，使用新建文件向导创建 helper 类  ``HibernateUtil.java`` 。

1. 右键单击 "Source Packages"（源包）节点并选择 "New"（新建）> "Other"（其他），打开新建文件向导。
2. 从 "Categories"（类别）列表中选择 "Hibernate"，从 "File Types"（文件类型）列表中选择 HibernateUtil.java。单击 "Next"（下一步）。
3. 键入 *HibernateUtil* 作为类名，并键入 *dvdrental* 作为包名。单击 "Finish"（完成）。

image::images/hibernate-util-wizard.png[title=""HibernateUtil" 向导的屏幕快照"]

单击 "Finish"（完成），此时  ``HibernateUtil.java``  将在编辑器中打开。由于不需要编辑该文件，因此可以关闭该文件。


== 生成 Hibernate 映射文件和 Java 类

在本教程中，您将使用一个 POJO（简单传统 Java 对象）来表示您将要用到的数据库中的每个表中的数据。Java 类指定表的列字段，并使用简单的 setter 和 getter 方法检索数据和写数据。要将该 POJO 映射到表，可使用 Hibernate 映射文件或使用类标注。

您可以使用 "Hibernate Mapping Files and POJOs from a Database"（通过数据库生成 Hibernate 映射文件和 POJO）向导根据数据库表创建多个 POJO 和映射文件。使用该向导时，选择希望从中创建 POJO 和映射文件的所有表，IDE 然后根据该数据库表生成文件并将映射条目添加到  ``hibernate.cfg.xml`` 。使用向导时可选择希望 IDE 生成的文件（比如，只生成 POJO）并选择代码生成选项（例如，生成使用 EJB 3 标注的代码）。

*注：*IDE 还具有可帮助您从头创建各个 POJO 和映射文件的向导。


=== 创建 Hibernate 逆向工程文件

如果要使用 "Hibernate Mapping Files and POJOs from a Database"（通过数据库生成 Hibernate 映射文件和 POJO）向导，您首先需要创建一个  ``hibernate.reveng.xml``  逆向工程文件。"Hibernate Mapping Files and POJOs from a Database"（通过数据库生成 Hibernate 映射文件和 POJO）向导需要  ``hibernate.reveng.xml``  和  ``hibernate.cfg.xml`` 。

通过使用逆向工程文件，您可以更好地控制数据库映射策略。Hibernate 逆向工程向导创建一个具有默认配置的逆向工程文件，可以在 XML 编辑器中编辑该文件。

要创建 Hibernate 逆向工程文件，请执行以下步骤。

1. 在 "Projects"（项目）窗口中右键单击 "Source Packages"（源包）节点，并选择 "New"（新建）> "Other"（其他）以打开新建文件向导。
2. 从 "Hibernate" 类别中选择 Hibernate 逆向工程向导。单击 "Next"（下一步）。
3. 指定  ``hibernate.reveng``  作为文件名，指定  ``src/java``  作为文件夹。单击 "Next"（下一步）。
4. 在 "Configuration File"（配置文件）下拉列表中选择  ``hibernate.cfg.xml`` （如果尚未选定）。
5. 从 "Available Tables"（可用表）中选择以下表，并单击 "Add"（添加）将表添加到 "Selected Tables"（选定的表）。
* actor
* category
* film
* film_actor
* film_category
* language

单击 "Finish"（完成）。

image::images/hibernate-reveng-wizard.png[title="新建 Hibernate 逆向工程向导"]

该向导生成一个  ``hibernate.reveng.xml``  逆向工程文件，并在编辑器中打开该文件。可将该逆向工程文件关闭，因为无需对其进行编辑。

有关使用  ``hibernate.reveng.xml``  文件的详细信息，请参见以下指南中的link:http://docs.jboss.org/tools/2.1.0.Beta1/hibernatetools/html/reverseengineering.html[+第 5 章“控制逆向工程”+]：link:http://docs.jboss.org/tools/2.1.0.Beta1/hibernatetools/html/[+《Hibernate 工具参考指南》+]。


=== 创建 Hibernate 映射文件和 POJO

可以使用 "Hibernate Mapping Files and POJOs from a Database"（通过数据库生成 Hibernate 映射文件和 POJO）向导来生成文件。该向导可以为您在向导中选择的每个表生成 POJO 和相应的映射文件。映射文件是 XML 文件，其中包含有关如何将表中的列映射到 POJO 中的字段的数据。您需要有  ``hibernate.reveng.xml``  和  ``hibernate.cfg.xml``  文件才能使用该向导。

要使用向导创建 POJO 和映射文件，请执行以下步骤。

1. 在 "Projects"（项目）窗口中右键单击 "Source Packages"（源包）节点，并选择 "New"（新建）> "Other"（其他）以打开新建文件向导。
2. 在 "Hibernate" 类别中选择 "Hibernate Mapping Files and POJOs from a Database"（通过数据库生成 Hibernate 映射文件和 POJO）。单击 "Next"（下一步）。
3. 确保在下拉列表中选择  ``hibernate.cfg.xml``  和  ``hibernate.reveng.xml``  文件。
4. 在 "General Settings"（常规设置）选项中选择 *JDK 5 Language Features*（JDK 5 语言功能）。
5. 确保选中了 *Domain Code*（域代码）和 *Hibernate XML Mappings*（Hibernate XML 映射）选项。
6. 选择 *dvdrental* 作为包名。单击 "Finish"（完成）。

image::images/hibernate-pojo-wizard2.png[title=""Generate Hibernate Mapping Files and POJOs"（生成 Hibernate 映射文件和 POJO）向导"]

单击 "Finish"（完成）后，IDE 生成 POJO 和 Hibernate 映射文件，并将字段映射到在  ``hibernate.reveng.xml``  中指定的列。IDE 也添加映射条目到  ``hibernate.cfg.xml``  中。


[source,xml]
----

<hibernate-configuration>
  <session-factory>
    <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
    <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/sakila</property>
    <property name="hibernate.connection.username">myusername</property>
    <property name="hibernate.connection.password">mypassword</property>
    <property name="hibernate.show_sql">true</property>
    <property name="hibernate.current_session_context_class">thread</property>
    <property name="hibernate.query.factory_class">org.hibernate.hql.classic.ClassicQueryTranslatorFactory</property>
    <mapping resource="dvdrental/FilmActor.hbm.xml"/>
    <mapping resource="dvdrental/Language.hbm.xml"/>
    <mapping resource="dvdrental/Film.hbm.xml"/>
    <mapping resource="dvdrental/Category.hbm.xml"/>
    <mapping resource="dvdrental/Actor.hbm.xml"/>
    <mapping resource="dvdrental/FilmCategory.hbm.xml"/>
  </session-factory>
</hibernate-configuration>
----

*注：*确认  ``mapping``  元素在  ``hibernate.cfg.xml``  文件中列在  ``property``  元素后面。

展开  ``dvdrental``  包查看由向导生成的文件。

image::images/hib-projectswindow.png[title="显示生成的 POJO 的 "Projects"（项目）窗口"]

如果要创建 Hibernate 映射文件以将特定表映射到特定类，您可以使用 Hibernate 映射向导。

有关使用  ``hibernate.reveng.xml``  文件的详细信息，请参见以下指南中的link:http://docs.jboss.org/hibernate/core/3.3/reference/en/html/mapping.html[+第 5 章“基本 O/R 映射”+]：link:http://docs.jboss.org/hibernate/stable/core/reference/en/html/[+《Hibernate 参考文档》+]。


== 创建  ``FilmHelper.java``  Helper 类

现在，您将在  ``dvdrental``  包中创建一个 helper 类，将用于在数据库中执行 Hibernate 查询。您将使用“Hibernate 查询语言（HQL）”编辑器来构造并测试用于检索数据的查询。测试了查询后，将在构造并运行查询的 helper 类中创建方法。然后，通过 JSF 受管 Bean 调用 helper 类中的方法。


=== 创建类

在此部分，使用新建文件向导在  ``dvdrental``  包中创建 helper 类  ``FilmHelper.java`` 。您将通过调用  ``HibernateUtil.java``  中的  ``getSessionFactory``  来创建一个 Hibernate 会话，并创建一些 helper 方法以创建查询来检索数据库中的数据。将从 JSP 页面中调用这些 helper 方法。

1. 右键单击  ``dvdrental``  源包节点，并选择 "New"（新建）> "Java Class"（Java 类）以打开新建文件向导。
2. 键入 *FilmHelper* 作为类名。
3. 确认已将 *dvdrental* 选作 "Package"（包）。单击 "Finish"（完成）。
4. 添加以下代码（粗体），创建一个 Hibernate 会话。

[source,java]
----

public class FilmHelper {

    *Session session = null;

    public FilmHelper() {
        this.session = HibernateUtil.getSessionFactory().getCurrentSession();
    }*

}
----


. 在编辑器中右键单击并选择 "Fix Imports"（修复导入）（Alt-Shift-I 组合键；在 Mac 上为 ⌘-Shift-I 组合键）以添加任何需要的 import 语句 ( ``org.hibernate.Session`` )，并保存更改。

现在将修改  ``FilmHelper.java``  以添加查询数据库的方法。


=== 使用 HQL 查询列举影片名称并检索演员

在本练习中，将创建一个 Hibernate 查询语言 (HQL) 查询，以便查询数据库以从 Film 表中检索影片名称列表。然后，添加一个方法以查询 Actor 和 Film_actor 表，以便获取特定影片中涉及的演员。

Film 表包含 1000 条记录，因此检索影片列表的方法应该能够基于  ``filmId``  主键检索记录。您将使用 HQL 编辑器构造和测试 HQL 查询。在创建正确的查询后，将在类中添加一个方法以生成相应的查询。

1. 在 "Projects"（项目）窗口中右键单击项目节点，然后选择 "Clean and Build"（清理并构建）。
2. 在 "Projects"（项目）窗口中右键单击  ``hibernate.cfg.xml``  并选择 "Run HQL Query"（运行 HQL 查询）以打开 HQL 查询编辑器。
3. 从工具栏的下拉列表中选择 hibernate.cfg。
4. 通过在编辑器中键入以下内容并单击工具栏中的 "Run HQL Query"（运行 HQL 查询）按钮 (image:images/run_hql_query_16.png[title=""Run HQL Query"（运行 HQL 查询）按钮"])，测试连接。

[source,java]
----

from Film
----

单击 "Run HQL Query"（运行 HQL 查询）可在 HQL 查询编辑器的底部窗口看到查询结果。

image::images/hibernate-hqleditor1.png[title=""Generate Hibernate Mapping Files and POJOs"（生成 Hibernate 映射文件和 POJO）向导"]

如果单击 SQL 按钮，可以看到起相同作用的 SQL 查询。


[source,java]
----

select film0_.film_id as col_0_0_ from sakila.film film0_
----


. 键入以下查询在 film id 为 100 到 200 的 Film 表中检索记录。

[source,java]
----

from Film as film where film.filmId between 100 and 200
----

结果窗口中显示了一个记录列表。测试过该查询可返回期待的结果后，可在 helper 类中使用该查询。



. 将下面的  ``getFilmTitles``  方法添加到  ``FilmHelper.java``  以检索影片，其中 film id 介于变量  ``startID``  和  ``endID``  所指定的特定范围之间。

[source,java]
----

public List getFilmTitles(int startID, int endID) {
    List<Film> filmList = null;
    try {
        org.hibernate.Transaction tx = session.beginTransaction();
        Query q = session.createQuery ("from Film as film where film.filmId between '"+startID+"' and '"+endID+"'");
        filmList = (List<Film>) q.list();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return filmList;
}
----


. 添加下面的  ``getActorsByID``  方法以检索特定影片中的演员。该方法将  ``filmId``  作为输入变量以构造查询。

[source,java]
----

public List getActorsByID(int filmId){
    List<Actor> actorList = null;
    try {
        org.hibernate.Transaction tx = session.beginTransaction();
        Query q = session.createQuery ("from Actor as actor where actor.actorId in (select filmActor.actor.actorId from FilmActor as filmActor where filmActor.film.filmId='" + filmId + "')");
        actorList = (List<Actor>) q.list();

    } catch (Exception e) {
        e.printStackTrace();
    }

    return actorList;
}
----


. 修复导入并保存更改。

修复导入时，要选择  ``java.util.List``  和  ``org.hibernate.Query`` 。


=== 添加额外的 helper 方法

现在将添加一些额外的 helper 方法，创建基于输入变量的查询。可在 HQL 查询编辑器中检查这些查询。

1. 添加以下方法以根据  ``filmId``  检索类别列表。

[source,java]
----

public Category getCategoryByID(int filmId){
    List<Category> categoryList = null;
    try {
        org.hibernate.Transaction tx = session.beginTransaction();
        Query q = session.createQuery("from Category as category where category.categoryId in (select filmCat.category.categoryId from FilmCategory as filmCat where filmCat.film.filmId='" + filmId + "')");
        categoryList = (List<Category>) q.list();

    } catch (Exception e) {
        e.printStackTrace();
    }

    return categoryList.get(0);
}
----


. 添加以下方法以根据  ``filmId``  检索单个影片。

[source,java]
----

public Film getFilmByID(int filmId){

    Film film = null;

    try {
        org.hibernate.Transaction tx = session.beginTransaction();
        Query q = session.createQuery("from Film as film where film.filmId=" + filmId);
        film = (Film) q.uniqueResult();
    } catch (Exception e) {
        e.printStackTrace();
    }

    return film;
}
----


. 添加以下方法以根据  ``langId``  检索影片语言。

[source,java]
----

public String getLangByID(int langId){

    Language language = null;

    try {
        org.hibernate.Transaction tx = session.beginTransaction();
        Query q = session.createQuery("from Language as lang where lang.languageId=" + langId);
        language = (Language) q.uniqueResult();
    } catch (Exception e) {
        e.printStackTrace();
    }

    return language.getName();
}
----


. 保存所做的更改。


== 创建 JSF 受管 Bean

在本练习中，将创建一个 JSF 受管 Bean。受管 Bean 中的方法用于显示 JSF 页面中的数据，以及访问 helper 类中的方法以检索记录。JSF 2.0 规范允许在 Bean 类中使用标注以将类标识为 JSF 受管 Bean，以及指定范围和 Bean 名称。

要创建受管 Bean，请执行以下步骤。

1. 右键单击  ``dvdrental``  源包节点，然后选择 "New"（新建）> "Other"（其他）。
2. 从 "JavaServer Faces" 类别中选择 "JSF Managed Bean"（JSF 受管 Bean）。单击 "Next"（下一步）。
3. 键入 *FilmController* 作为类名。

在调用受管 Bean 中的方法时，将使用 Bean 名称  ``filmController``  作为 JSF 页面  ``index.xhtml``  中的  ``inputText``  和  ``commandButton``  的值。



. 选择 *dvdrental* 作为包。


. 键入 *filmController* 作为将用于受管 Bean 的名称。


. 将 "Scope"（范围）设置为 "Session"（会话）。单击 "Finish"（完成）。

image::images/hib-newmanagedbean.png[title="新建 JSF 受管 Bean 向导"]

单击 "Finish"（完成），此时 IDE 创建 Bean 类并在编辑器中打开该类。IDE 添加了  ``@ManagedBean``  和  ``@SessionScoped``  标注。


[source,java]
----

@ManagedBean
@SessionScoped
public class FilmController {

    /** Creates a new instance of FilmController */
    public FilmController() {
    }

}

----

*注：*请注意，未明确指定受管 Bean 的名称。默认情况下，Bean 的名称与类名相同，并以小写字母开头。如果您希望 Bean 的名称与类名不同，则可将该名称显式指定为  ``@ManagedBean``  标注的参数（例如， ``@ManagedBean(name="myBeanName"）`` 。

1. 将以下字段（以粗体显示）添加到类中。

[source,java]
----

@ManagedBean
@SessionScoped
public class FilmController {
    *int startId;
    int endId;
    DataModel filmTitles;
    FilmHelper helper;
    private int recordCount = 1000;
    private int pageSize = 10;

    private Film current;
    private int selectedItemIndex;*
}
----


. 添加以下代码（以粗体显示），创建 FilmController 实例并检索影片。

[source,java]
----

    /** Creates a new instance of FilmController */
    public FilmController() {
        *helper = new FilmHelper();
        startId = 1;
        endId = 10;
    }

    public FilmController(int startId, int endId) {
        helper = new FilmHelper();
        this.startId = startId;
        this.endId = endId;
    }

    public Film getSelected() {
        if (current == null) {
            current = new Film();
            selectedItemIndex = -1;
        }
        return current;
    }


    public DataModel getFilmTitles() {
        if (filmTitles == null) {
            filmTitles = new ListDataModel(helper.getFilmTitles(startId, endId));
        }
        return filmTitles;
    }

    void recreateModel() {
        filmTitles = null;
    }*
----


. 添加用于显示表和导航页面的以下方法。*    public boolean isHasNextPage() {
        if (endId + pageSize <= recordCount) {
            return true;
        }
        return false;
    }

    public boolean isHasPreviousPage() {
        if (startId-pageSize > 0) {
            return true;
        }
        return false;
    }

    public String next() {
        startId = endId+1;
        endId = endId + pageSize;
        recreateModel();
        return "index";
    }

    public String previous() {
        startId = startId - pageSize;
        endId = endId - pageSize;
        recreateModel();
        return "index";
    }

    public int getPageSize() {
        return pageSize;
    }

    public String prepareView(){
        current = (Film) getFilmTitles().getRowData();
        return "browse";
    }
    public String prepareList(){
        recreateModel();
        return "index";
    }
*

返回 "index" 或 "browse" 的方法将提示 JSF 导航处理程序尝试打开名为  ``index.xhtml``  或  ``browse.xhtml``  的页面。JSF 2.0 规范允许在使用 Facelets 技术的应用程序中使用隐式导航规则。此应用程序中，没有在  ``faces-config.xml``  中配置任何导航规则。导航处理程序将尝试在应用程序中查找合适的页面。



. 添加以下方法以访问 helper 类，以便检索其他影片详细信息。*    public String getLanguage() {
        int langID = current.getLanguageByLanguageId().getLanguageId().intValue();
        String language = helper.getLangByID(langID);
        return language;
    }

    public String getActors() {
        List actors = helper.getActorsByID(current.getFilmId());
        StringBuffer totalCast = new StringBuffer();
        for (int i = 0; i < actors.size(); i++) {
            Actor actor = (Actor) actors.get(i);
            totalCast.append(actor.getFirstName());
            totalCast.append(" ");
            totalCast.append(actor.getLastName());
            totalCast.append("  ");
        }
        return totalCast.toString();
    }

    public String getCategory() {
        Category category = helper.getCategoryByID(current.getFilmId());
        return  category.getName();
    }*

[source,java]
----


----


. 修复导入（Ctrl-Shift-I 组合键）并保存更改。

您可以在编辑器中使用代码完成以帮助键入代码。


== 创建 Web 页面

在本练习中，将创建两个 Web 页以显示数据。您将修改 IDE 生成的  ``index.xhtml``  以添加一个表，以便显示数据库中的影片。然后创建  ``browse.xhtml`` ，以便在单击表中的 "View" 链接时显示影片的详细信息。还会创建一个 JSF 模板页面以供  ``index.xhtml``  和  ``browse.xhtml``  使用。

有关使用 JSF 2.0 和 Facelets 模板的更多信息，请参见 link:jsf20-intro.html[+JavaServer Faces 2.0 简介+]。


=== 创建  ``template.xhtml`` 

首先，创建 JSF Facelets 模板  ``template.xhtml`` ，在创建  ``index.xhtml``  和  ``browse.xhtml``  页面时将使用该模板。

1. 在 "Projects"（项目）窗口中右键单击 DVDStore 项目节点，然后选择 "New"（新建）> "Other"（其他）。
2. 在 "JavaServer Faces" 类别中选择 "Facelets Template"（Facelets 模板）。单击 "Next"（下一步）。
3. 键入 *template* 作为文件名，并选择第一个 CSS 布局样式。
4. 单击 "Finish"（完成）。

单击 "Finish"（完成），此时  ``template.xhtml``  文件将在编辑器中打开。该模板包含以下默认代码。


[source,html]
----

<h:body>

    <div id="top" class="top">
        <ui:insert name="top">Top</ui:insert>
    </div>

    <div id="content" class="center_content">
        <ui:insert name="content">Content</ui:insert>
    </div>

</h:body>
----


. 修改  ``<ui:insert>``  元素，以将生成的默认名称更改为 "body"。

[source,html]
----

<div id="content" class="center_content">
        <ui:insert name="*body*">Content</ui:insert>
</div>
----


. 保存所做的更改。

 ``index.xhtml``  和  ``browse.xhtml``  中的  ``<ui:define name="body">``  元素包含的内容将插入到使用模板中的  ``<ui:insert name="body">Content</ui:insert>``  指定的位置。


=== 修改  ``index.xhtml`` 

在创建 Web 应用程序时，IDE 将自动生成  ``index.xhtml``  页面。在本练习中，将修改该页面以显示影片名称列表。JSF 页面调用 JSF 受管 Bean FilmController 中的方法以检索影片列表，然后显示一个包含影片名称和说明的表格。

1. 展开 "Projects"（项目）窗口的 "Web Pages"（Web 页）文件夹，并在编辑器中打开  ``index.xhtml`` 。

新建项目向导生成了以下默认  ``index.xhtml``  页面。


[source,xml]
----

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
<h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        Hello from Facelets
    </h:body>
</html>
----


. 修改该页面以使用 JSF  ``<ui:composition>``  和  ``<ui:define>``  元素，并添加  ``<h:form>``  元素。

[source,xml]
----

<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://java.sun.com/jsf/html"
  *xmlns:ui="http://java.sun.com/jsf/facelets">
    <ui:composition template="./template.xhtml">
        <ui:define name="body">
            <h:form>

            </h:form>
        </ui:define>
    </ui:composition>*
</html>
----

开始键入标记时，IDE 将添加  ``xmlns:ui="http://java.sun.com/jsf/facelets"``  标记库声明。

 ``<ui:composition>``  和  ``<ui:define>``  元素将与所创建的页面模板结合使用。 ``<ui:composition>``  元素引用此页面将使用的模板的位置。 ``<ui:define>``  元素引用包含的代码将占用的模板位置。



. 添加以下导航链接以调用 JSF 受管 Bean 中的  ``previous``  和  ``next``  方法。

[source,xml]
----

    <ui:define name="body">
            <h:form>
                *<h:commandLink action="#{filmController.previous}" value="Previous #{filmController.pageSize}" rendered="#{filmController.hasPreviousPage}"/> 
                <h:commandLink action="#{filmController.next}" value="Next #{filmController.pageSize}" rendered="#{filmController.hasNextPage}"/> *
            </h:form>
    </ui:define>
----


. 添加以下  ``dataTable``  元素（以粗体显示）来生成表，以便显示检索到的条目。

[source,xml]
----

            <h:form styleClass="jsfcrud_list_form">
                <h:commandLink action="#{filmController.previous}" value="Previous #{filmController.pageSize}" rendered="#{filmController.hasPreviousPage}"/> 
                <h:commandLink action="#{filmController.next}" value="Next #{filmController.pageSize}" rendered="#{filmController.hasNextPage}"/> 
                *<h:dataTable value="#{filmController.filmTitles}" var="item" border="0" cellpadding="2" cellspacing="0" rowClasses="jsfcrud_odd_row,jsfcrud_even_row" rules="all" style="border:solid 1px">
                    <h:column>
                        <f:facet name="header">
                            <h:outputText value="Title"/>
                        </f:facet>
                        <h:outputText value="#{item.title}"/>
                    </h:column>
                    <h:column>
                        <f:facet name="header">
                            <h:outputText value="Description"/>
                        </f:facet>
                        <h:outputText value="#{item.description}"/>
                    </h:column>
                    <h:column>
                        <f:facet name="header">
                            <h:outputText value=" "/>
                        </f:facet>
                        <h:commandLink action="#{filmController.prepareView}" value="View"/>
                    </h:column>
                </h:dataTable>
                <br/>*
            </h:form>

----


. 保存所做的更改。

现在，index 页面将显示数据库中的影片名称列表。表中的每一行都包含 "View" 链接，用于调用受管 Bean 中的  ``prepareView``  方法。 ``prepareView``  方法返回 "browse" 并打开  ``browse.xhtml`` 。

*注：*键入  ``<f:facet>``  标记时，IDE 将添加  ``xmlns:f="http://java.sun.com/jsf/core``  标记库声明。*确认已在文件中声明标记库。*

 


=== 创建  ``browse.xhtml`` 

现在，将创建  ``browse.xhtml``  页面以显示所选影片的详细信息。可以使用 Facelets 模板客户端向导基于所创建的 JSF Facelets 模板  ``template.xhtml``  创建该页面。

1. 在 "Projects"（项目）窗口中右键单击 DVDStore 项目节点，然后选择 "New"（新建）> "Other"（其他）。
2. 在 "JavaServer Faces" 类别中选择 "Facelets Template Client"（Facelets 模板客户端）。单击 "Next"（下一步）。

image::images/hib-faceletsclient.png[title="新建文件向导中的 "Facelets Template Client"（Facelets 模板客户端）文件类型"]


. 键入 *browse* 作为文件名。


. 通过单击 "Browse"（浏览）打开 "Browse Files"（浏览文件）对话框，以查找页面模板。


. 展开 "Web Pages"（Web 页）文件夹并选择  ``template.xhtml`` 。单击 "Select File"（选择文件）。

image::images/hib-browsetemplate.png[title="在 "Browse Files"（浏览文件）对话框中选择模板"]


. 选择 *<ui:composition>* 作为生成的根标记。单击 "Finish"（完成）。

单击 "Finish"（完成），此时  ``browse.xhtml``  文件将在编辑器中打开，其中包含以下代码。


[source,xml]
----

<ui:composition xmlns:ui="http://java.sun.com/jsf/facelets"
    template="./template.xhtml">

    <ui:define name="top">
        top
    </ui:define>

    <ui:define name="body">
        body
    </ui:define>

</ui:composition>
----

您可以看到新文件指定了  ``template.xhtml``  文件，并且  ``<ui:define>``  标记具有属性  ``name="body"`` 



. 在  ``<ui:define>``  标记之间添加以下代码（以粗体显示）以创建窗体，并调用受管 Bean FilmController 中的方法以检索数据和填充窗体。

[source,xml]
----

<ui:composition xmlns:ui="http://java.sun.com/jsf/facelets"
    template="./template.xhtml"
    *xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"*>

        <ui:define name="top">
            top
        </ui:define>

        <ui:define name="body">
        *
            <h:form>
                <h:panelGrid columns="2">
                    <h:outputText value="Title:"/>
                    <h:outputText value="#{filmController.selected.title}" title="Title"/>
                    <h:outputText value="Description"/>
                    <h:outputText value="#{filmController.selected.description}" title="Description"/>
                    <h:outputText value="Genre"/>
                    <h:outputText value="#{filmController.category}"/>

                    <h:outputText value="Cast"/>
                    <h:outputText value="#{filmController.actors}"/>


                    <h:outputText value="Film Length"/>
                    <h:outputText value="#{filmController.selected.length} min" title="Film Length"/>

                    <h:outputText value="Language"/>
                    <h:outputText value="#{filmController.language}" title="Film Length"/>

                    <h:outputText value="Release Year"/>
                    <h:outputText value="#{filmController.selected.releaseYear}" title="Release Year">
                        <f:convertDateTime pattern="MM/dd/yyyy" />
                    </h:outputText>
                    <h:outputText value="Rental Duration"/>
                    <h:outputText value="#{filmController.selected.rentalDuration}" title="Rental DUration"/>
                    <h:outputText value="Rental Rate"/>
                    <h:outputText value="#{filmController.selected.rentalRate}" title="Rental Rate"/>
                    <h:outputText value="Replacement Cost"/>
                    <h:outputText value="#{filmController.selected.replacementCost}" title="Replacement Cost"/>
                    <h:outputText value="Rating"/>
                    <h:outputText value="#{filmController.selected.rating}" title="Rating"/>
                    <h:outputText value="Special Features"/>
                    <h:outputText value="#{filmController.selected.specialFeatures}" title="Special Features"/>
                    <h:outputText value="Last Update"/>
                    <h:outputText value="#{filmController.selected.lastUpdate}" title="Last Update">
                        <f:convertDateTime pattern="MM/dd/yyyy HH:mm:ss" />
                    </h:outputText>
                </h:panelGrid>
                <br/>
                <br/>
                <h:commandLink action="#{filmController.prepareList}" value="View All List"/>
                <br/>
            </h:form>
*
        </ui:define>
    </ui:composition>
</html>
----

您可以看到  ``browse.xhtml``  和  ``index.xhtml``  使用相同的页面模板。



. 保存所做的更改。


== 运行项目

应用程序的基础部分现已完成。现在，您可以运行应用程序以检查其是否正常运行。

1. 在主工具栏中单击 "Run Main Project"（运行主项目）或在 "Projects"（项目）窗口中右键单击 DVDStore 应用程序节点并选择 "Run"（运行）。

IDE 保存所有更改过的文件，构建应用程序并将应用程序部署到服务器。IDE 将打开浏览器窗口并指向 URL  ``http://localhost:8080/DVDStore/``  以显示影片列表。

image::images/hib-browser1.png[title="在索引页上显示影片列表的浏览器的屏幕快照"]


. 在浏览器中，单击 "View" 加载  ``browse.xhtml``  以查看影片详细信息。


=== 下载解决方案项目

您可以采用下列方法下载本教程的解决方案（作为一个项目）。

* 下载link:https://netbeans.org/projects/samples/downloads/download/Samples/JavaEE/DVDStoreEE6.zip[+已完成项目的 zip 档案文件+]。
* 通过执行以下步骤从 NetBeans 样例检出项目源代码：
1. 从主菜单中选择 "Team"（团队开发）> "Subversion" > "Checkout"（检出）。
2. 在 "Checkout"（检出）对话框中，输入以下资源库 URL：
 ``https://svn.netbeans.org/svn/samples~samples-source-code`` 
单击 "Next"（下一步）。


. 单击 "Browse"（浏览）以打开 "Browse Repostiory Folders"（浏览资源库文件夹）对话框。


. 展开根目录节点，然后选择 *samples/javaee/DVDStoreEE6*。单击 "OK"（确定）。


. 指定用于存储源代码的 "Local Folder"（本地文件夹）。


. 单击 "Finish"（完成）。

单击 "Finish"（完成），此时 IDE 会将本地文件夹初始化为 Subversion 资源库，并检出项目源代码。



. 在完成检出操作后将会显示一个对话框，在该对话框中单击 "Open Project"（打开项目）。

*注：*有关安装 Subversion 的更多信息，请参见 link:../ide/subversion.html[+NetBeans IDE 中的 Subversion 指南+]中有关link:../ide/subversion.html#settingUp[+设置 Subversion+] 的部分。


=== 疑难解答

教程应用程序出现的大多数问题是由于 GlassFish Server Open Source Edition 与 MySQL 数据库服务器之间的通信发生问题。如果应用程序显示不正确或者您收到服务器错误，您可能需要查看link:mysql-webapp.html[+使用 MySQL 数据库创建简单 Web 应用程序+]教程或link:../ide/mysql.html[+连接到 MySQL 数据库+]教程的link:mysql-webapp.html#troubleshoot[+疑难解答+]部分。

如果您是首次部署了使用 MySQL 数据库的应用程序，则在下载和运行解决方案项目时，可能会在 "Output"（输出）窗口中看到以下错误。


[source,java]
----

    SEVERE: JDBC Driver class not found: com.mysql.jdbc.Driver
    java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
	at org.glassfish.web.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1509)

        [...]

	at java.lang.Thread.run(Thread.java:680)

    SEVERE: Initial SessionFactory creation failed.org.hibernate.HibernateException: JDBC Driver class not found: com.mysql.jdbc.Driver
    INFO: cleaning up connection pool: null
    INFO: Domain Pinged: stable.glassfish.org
----

在浏览器窗口中，您可能会看到  ``java.lang.ExceptionInInitializerError``  以及以下堆栈跟踪。


[source,java]
----

    java.lang.ExceptionInInitializerError
	at dvdrental.HibernateUtil.<clinit>(HibernateUtil.java:28)
	...
    Caused by: org.hibernate.HibernateException: JDBC Driver class not found: com.mysql.jdbc.Driver
	
	... 
    Caused by: java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
	... 
----

输出消息指出未找到 MySQL 数据库的 JDBC 驱动程序。最可能的原因是，您需要将 MySQL JDBC 驱动程序文件添加到 GlassFish Server 安装中。要确认缺少此驱动程序，请导航至本地系统上的  ``_GLASSFISH-INSTALL_/glassfish/domains/domain1/lib``  目录（其中 _GLASSFISH-INSTALL_ 是 GlassFish 安装目录）。如果  ``domain1/lib``  目录不包含 JDBC 驱动程序文件（例如 `mysql-connector-java-5.1.13-bin.jar`），则您需要将 JDBC 驱动程序复制到此目录中。安装服务器时，未将 MySQL JDBC 驱动程序添加到 GlassFish 安装中。

通过执行以下步骤，可以将 MySQL JDBC 驱动程序的副本添加到 GlassFish 安装中。

1. 下载 link:http://dev.mysql.com/downloads/connector/j/[+MySQL Connector/J JDBC 驱动程序+]。
2. 提取此驱动程序并将驱动程序文件（例如 `mysql-connector-java-5.1.13-bin.jar`）复制到 GlassFish 安装的  ``domain1/lib``  目录中。

或者，当您使用 IDE 创建使用 MySQL 数据库的应用程序时，IDE 也可以在部署项目时自动将捆绑的 MySQL JDBC 驱动程序复制到 GlassFish Server（如果需要）。要确认 IDE 将复制所需的 JDBC 驱动程序，请从主菜单中选择“工具”>“服务器”以打开“服务器管理器”，然后确认为 GlassFish Server 选定了“启用 JDBC 驱动程序部署”选项。

在创建并部署使用 MySQL 数据库的 Web 应用程序之后，如果导航至本地 GlassFish 安装的 `domain1/lib` 目录，您将会看到该目录包含 JDBC 驱动程序文件。

link:/about/contact_form.html?to=3&subject=Feedback:%20Using%20Hibernate%20in%20a%20Web%20Application[+发送有关此教程的反馈意见+]



== 另请参见

* link:jsf20-support.html[+NetBeans IDE 中的 JSF 2.x 支持+]
* link:jsf20-intro.html[+JavaServer Faces 2.x 简介+]
* link:../../trails/java-ee.html[+Java EE 和 Java Web 学习资源+]
