版本: 5/12/06
此教程将指导您完成使用 EJB 3.0 Java 持续性 API (JPA) 进行 Web 应用程序的开发、打包和部署的基本步骤。
在应用程序中,Java Server Faces (JSF) 表示层将利用 JPA 获得 EJB 3.0 容器以外的持续性。
图 1-1 显示了此教程使用的对象模型。
有关 JPA 的更多信息,请参见:
JSR-220 Enterprise JavaBeans v3.0 Java 持续性 API 规范
关系数据库
您可以使用任意的最新的关系数据库。
本教程使用的数据库是 Oracle 数据库快捷版:
请使用为您的数据库推荐的 JDBC 驱动程序。
Oracle 数据库快捷版的 JDBC 驱动程序位于 <ORACLE_HOME>\jdbc\lib 中。
Web 容器:
您可以使用任意的 Web 容器。
本教程假设您使用了以下 Web 容器之一:
单击上面的链接将下载一个 TopLink JPA 安装程序 JAR 文件,如 glassfish-persistence-installer-X.X-bXX.jar。
在开始本教程之前,您必须安装并配置好所需的软件:
安装教程应用程序。
将 order-jsf-jpa.zip 文件解压缩到您计算机上的一个目录中。
这是 <TUTORIAL_HOME> 目录。
解压缩完成后,应该包括以下文件:
jpa-example.war - 用于进行部署的完整应用程序包。
jpa-example-src.jar - 所有非持续性源文件。
persistence-unit-src.jar - 所有持续性源文件,其中包括 persistence.xml 文件。
在 <TUTORIAL_HOME> 中创建一个子目录 <WAR_HOME>,并将 jpa-example.war 文件解压缩到该子目录中。
有关教程应用程序的更多信息,请参见 了解教程应用程序。
安装并设置您的关系数据库:
将关系数据库 JDBC 驱动程序添加到教程应用程序 WAR 文件子目录 <TUTORIAL_HOME>\<WAR_HOME>\WEB-INF\lib 中。
安装并设置 Web 容器:
安装 TopLink JPA:
将 TopLink JPA 安装程序 JAR 文件转移到临时目录中。
通过在命令行中输入以下命令来执行您所下载的 TopLink JPA 安装程序 JAR 文件(必须使用 JDK 1.5):
java -jar glassfish-persistence-installer-X.X-bXX.jar
向下滚动到许可协议的末尾,单击 Accept。
安装程序会解开 README、许可文件和 TopLink Essential JAR 文件,如下所示:
glassfish-persistence\README glassfish-persistence\3RD-PARTY-LICENSE.txt glassfish-persistence\toplink-essentials.jar glassfish-persistence\toplink-essentials-agent.jar glassfish-persistence\CDDLv1.0.txt
将 toplink-essentials.jar 和 toplink-essentials-agent.jar 添加到教程应用程序 WAR 文件子目录 <TUTORIAL_HOME>\<WAR_HOME>\WEB-INF\lib 中。
教程应用程序及其所有必要组件都包含在解压缩到 <TUTORIAL_HOME>\<WAR_HOME> 目录的名为 jpa-example.war 的 WAR 文件中(参见安装与配置)。
示例 1-1 显示了此 WAR 文件的结构,表 1-1 描述了该文件中每个重要的子目录。
示例 1-1 jpa-example.war 文件的结构
images WEB-INF classes/ oracle/toplink/jpa/example/inventory/ services/ InventoryService.class OrderService.class services/impl/ JPAResourceBean.class ManagedInventoryBean.class ManagedOrderBean.class tools/ DDLGenerator.class Populator.class ui/ InventoryManagerBean.class lib/ - JSF JARs - JDBC driver JARs toplink-essentials.jar toplink-essentials-agent.jar jpa-demo.jar faces-config.xml web.xml *.jsp
表 1-1 jpa-example.war 文件的子目录
| 子目录 | 说明 |
|---|---|
|
|
images 目录中包含教程应用程序使用的图像。 |
|
|
相应的 Java 源文件位于 |
|
|
相应的 Java 源文件位于 |
|
|
|
|
|
|
|
|
|
以下过程介绍了创建教程应用程序时的重要步骤:
批注是使用元数据对 Java 源代码进行修饰的一种简单、明确的方法,该元数据将被编译进相应的 Java 类文件,在运行时期间由 JPA 持续性提供器对其进行解释以管理 JPA 行为。
使用 JPA 批注修饰持续性类可以为 JPA 持续性提供程序指明哪些类需要持续性,并且可以指定持续性类的详细内容。
在大多数情况下,您可以使用 JPA 的默认值。本教程显示了必需的批注和一些普通的可选批注。
有关更多信息,请参见 JPA 批注引用。
在本教程中,为实体使用批注的目的是:
在本教程中,以下各类都由 @Entity 批注修饰:
oracle.toplink.jpa.example.inventory.model.Item
oracle.toplink.jpa.example.inventory.model.Inventory
oracle.toplink.jpa.example.inventory.model.Order
示例 1-2 显示了 Item 类的批注。@Entity 批注指示 JPA 持续性供应商管理这些类的持续性。
默认情况下,一个实体的名称就是该实体所属类的名称。您可以使用 @Entity 的属性 name 为实体指定其它的名称。
默认情况下,JPA 持续性供应程序使用实体名称(参见指定持续性类:@Entity)作为关系数据库表的名称。
在本教程中,TopLink JPA 持续性供应程序将基于此默认名称为 Item 和 Inventory 实体创建数据库表:即,为 ITEM 实体创建表 Item,为 INVENTORY 实体创建表 Inventory。
然而,您不能为 Order 实体创建名为 ORDER 的表,因为此名称是大多数关系数据库中的保留字。在此情况下,我们使用 @Table 批注覆盖默认的表名,如示例 1-3 所示。
示例 1-3 在 Order.java 中使用 @Table
@Entity
// specify table name as the default will be ORDER which is a reserved word
@Table(name="ORDER_TABLE")
...
public class Order {
...
}
或者,我们可以使用 @Entity 的属性 name 更改 Order 类的实体名称(指定持续性类:@Entity)。
在所有情况下,每个表中都将包含相应实体的所有持续性字段。JPA 指出,除非经过 @Transient 批注的修饰,否则一个实体的所有字段都是持续性字段。
每个实体都必须拥有一个主键:经过 @Id 批注修饰的字段或属性。
如示例 1-4 所示,Order 类的字段 orderId 被指定为实体的主键。@GeneratedValue 批注指示 JPA 持续性供应程序负责生成此字段唯一标识符的值,并对这些值进行管理和排序。
如示例 1-5 所示,Inventory 类的字段 id 被指定为实体的主键。如 Inventory 方法 setItem 所示,由于 Inventory 实体从其 Item 中获得主键的值(参见示例 1-5),所以此示例中不使用 @GeneratedValue 批注。有关 @Column 批注的更多信息,参见指定数据库列的属性:@Column。
示例 1-5 在 Inventory.java 中使用 @Id
public class Inventory {
@Id
@Column(insertable=false, updatable=false)
protected long id;
...
public void setItem(Item item) {
this.item = item;
this.id = item.getSKU();
}
...
}
如示例 1-6 所示,Item 类的属性 getSKU 被指定为实体的主键。通常情况下,您可以批注字段(如 Order.java 和 Inventory.java 中的批注)或批注与字段相关的属性(如示例 1-6 所示)。@GeneratedValue 批注指示 JPA 持续性供应程序负责排序:生成此字段唯一标识符的值,并对这些值进行管理。
默认情况下,JPA 指定每个实体的每个字段都对应该实体的关系数据库表中的一列,其中列的名称和类型分别对应字段的名称和类型。
您可以使用 @Column 批注覆盖这种默认行为。
如示例 1-7 所示,Inventory 类使用 @Column 批注针对字段 id 调整关系数据库的列。如 Inventory 方法 setItem 所示,由于 Inventory 实体从其 Item 中获得主键的值(参见示例 1-5),所以我们必须确保永远不在数据库中覆盖该值。因此,我们使用 @Column 批注将此列配置为不可插入且不可更新的列。这意味着,JPA 持续性供应程序不将此列包含在 SQL INSERT 或 SQL UPDATE 语句中。
默认情况下,JPA 持续性供应程序将自动管理基本映射(大部分 Java 基元类型、基元类型的包装和枚举类型)和关系映射。
您可以使用 @Basic 批注调整基本映射。
也可以使用以下批注调整关系映射:
@OneToOne 批注修饰 Inventory 类的字段 item ,指定 Inventory 与 Item 之间的关系(如示例 1-8 所示)。@JoinColumn 批注指示 JPA 持续性供应程序,引用 Item 的外键列是名为 ID 的 Inventory 列。
默认情况下,JPA 持续性供应程序假定由应用程序负责数据一致性。
Oracle 建议您指定作为乐观锁定值的实体类型的版本字段或属性,从而使用 @Version 批注来启用 JPA 持续性供应程序管理的乐观锁定。
如示例 1-9 所示,@Version 批注修饰 Order 类的字段 version,将其指定为乐观锁定字段。
如示例 1-10 所示,@Version 批注修饰 Inventory 类的字段 version,将其指定为乐观锁定字段。
示例 1-10 在 Inventory.java 中使用 @Version
public class Inventory {
...
@Version
protected int version;
...
}
如示例 1-11 所示,@Version 批注修饰 Item 类的属性 setVersion,将与该属性对应的字段 version 指定为乐观锁定字段。
在一个 JPA 应用程序中,您可以使用 EntityManager 实时动态地创建 JPA 查询语言查询,或者您也可以使用 @NamedQuery 和 @NamedQueries 批注预定义这些查询,并根据名称执行查询(参见第 4 步:使用命名查询和动态查询)。这对于经常使用或复杂的查询非常方便。
在本教程中,Inventory.java 定义了一个命名查询(如示例 1-12 所示)。
示例 1-12 在 Inventory.java 中使用 @NamedQuery
@NamedQuery( name="inventoryForCategory", query="SELECT i FROM Inventory i WHERE i.item.category = :category and i.quantity <= :maxQuantity" )
如果您想定义两个或更多的命名查询,您必须像 Order.java 那样使用 @NamedQueries 批注(参见示例 1-13)。
示例 1-13 在 Order.java 中使用 @NamedQueries
@NamedQueries({
@NamedQuery(
name="shippedOrdersForItem",
query="SELECT o FROM Order o JOIN o.item i WHERE i.sKU = :itemId and o.arrivalDate is not null"
),
@NamedQuery(
name="pendingOrdersForItem",
query="SELECT o FROM Order o WHERE o.item.sKU = :itemId and o.arrivalDate is null"
)
})
您还可以创建原生 SQL 查询(参见 @NamedNativeQuery 和 @NamedNativeQueries)。
实体管理器是您用来对实体实施基本持续性操作(创建、读取、更新和删除)的主要 JPA 界面。
一个持续性单元通过对实体管理器供应商、配置属性和持续性管理的类等详细内容进行逻辑分组来定义实体管理器的配置。
每个持续性单元都必须拥有名称。一个指定的 EJB-JAR、WAR、EAR 或应用程序客户端 JAR 中只能存在给定名称的一个持续性单元(参见打包 Persistence.xml 文件)。在获取实体管理器工厂时,您根据名称来指定持续性单元(参见获取实体管理器工厂)。
持续性单元在 persistence.xml 文件中定义。
在本教程中,persistence.xml 文件包含在 <TUTORIAL_HOME>\<WAR_HOME>\WEB-INF\lib\jpa-demo.jar 中。在此 persistence.xml 文件中,我们定义了示例 1-14 显示的持续性单元。
示例 1-14 Persistence.xml 中的持续性单元
... <persistence-unit name="default" transaction-type="RESOURCE_LOCAL"> <provider> oracle.toplink.essentials.PersistenceProvider </provider> <exclude-unlisted-classes>false</exclude-unlisted-classes> <properties> <property name="toplink.jdbc.driver" value="oracle.jdbc.OracleDriver"/> <property name="toplink.jdbc.url" value="jdbc:oracle:thin:@myhost:l521:MYSID"/> <property name="toplink.jdbc.password" value="tiger"/> <property name="toplink.jdbc.user" value="scott"/> </properties> </persistence-unit> ...
此持续性单元的名称为 default。
它的 transaction-type 为 RESOURCE_LOCAL,表示此持续性单元的实体管理器不参与 JTA 事务。
它使用供应商 oracle.toplink.essentials.PersistenceProvider(javax.persistence.EntityManagerFactory 的实例)。
通过将 exclude-unlisted-classes 设置为 false,此持续性单元管理其范围内的所有实体:即,所有 Order.class、Inventory.class 和 Item.class。
最后要设置的是持续性单元属性。您可以使用持续性单元属性调整底层 JPA 持续性供应程序,并为与持续性单元相关联的底层关系数据库指定连接详情。
要配置持续性单元:
将 jpa-demo.jar 文件解压缩到一个临时目录中。
编辑 persistence.xml 文件。
设置以下属性以匹配您的关系数据库:
<property name="toplink.jdbc.driver" value="oracle.jdbc.OracleDriver"/> <property name="toplink.jdbc.url" value="jdbc:oracle:thin:@myhost:l521:MYSID"/> <property name="toplink.jdbc.password" value="tiger"/> <property name="toplink.jdbc.user" value="scott"/>
再次压缩 jpa-demo.jar 文件。
将修改过的 jpa-demo.jar 文件复制到 <TUTORIAL_HOME>\<WAR_HOME>\WEB-INF\lib 目录中,覆盖原有的 jpa-demo.jar。
有关 TopLink JPA 持续性供应程序扩展内容的更多信息,参见 TopLink JPA 扩展参考。
有关 persistence.xml 文件的更多信息,参见 JSR-000220 企业 JavaBeans v3.0 JPA 规范,第 6.3 小节。
管理教程用户界面的主要资源是 oracle.toplink.jpa.example.inventory.ui.InventoryManagerBean。该资源中包含对以下各类的引用,这些类使用 JPA 实施此应用程序提供的服务:
oracle.toplink.jpa.example.inventory.services.impl.ManagedOrderBean(实施 oracle.toplink.jpa.example.inventory.services.OrderService)
oracle.toplink.jpa.example.inventory.services.impl.ManagedInventoryBean(实施 oracle.toplink.jpa.example.inventory.services.InventoryService)
这两个类显示了使用 JPA 进行以下操作的方法:
ManagedOrderBean 和 ManagedInventoryBean 都使用辅助类实例 oracle.toplink.jpa.example.inventory.services.impl.JPAResourceBean 为名为 default 的持续性单元(我们在 persistence.xml 文件中定义的持续性单元)获取实体管理器工厂的实例(参见第 2 步:配置持续性单元)。示例 1-15 显示了获取实体管理器工厂的方法。
示例 1-15 获取实体管理器工厂
public EntityManagerFactory getEMF (){
if (emf == null){
emf = Persistence.createEntityManagerFactory("default");
}
return emf;
}
一旦获取了实体管理器工厂,ManagedOrderBean 和 ManagedInventoryBean 类就会使用该实体管理器工厂获取一个实体管理器,以便执行所有的基本持续性操作(创建、读取、更新和删除)。
示例 1-16 显示了 ManagedOrderBean 使用其 EntityManager 创建新的 Order 实体的方法。
示例 1-17 显示了 ManagedOrderBean 使用其 EntityManager 根据主键读取当前 Order 实体的方法。
示例 1-18 显示了 ManagedOrderBean 使用其 EntityManager 更新当前 Order 实体的方法。在提交本地事务时,保留对 Order 实体进行的所有更改。
示例 1-18 在 ManagedOrderBean 中更新 Order 实体
public void alterOrderQuantity(long orderId, int newQuantity){
EntityManager em = jpaResourceBean.getEMF().createEntityManager();
try{
em.getTransaction().begin();
Order order = em.find(Order.class, orderId);
order.setQuantity(newQuantity);
em.getTransaction().commit();
}finally{
em.close();
}
}
示例 1-19 显示了 ManagedOrderBean 使用其 EntityManager 根据主键删除当前 Order 实体的方法。
示例 1-19 删除 ManagedOrderBean 中的 Order 实体
public void requestCancelOrder(long orderId){
EntityManager em = jpaResourceBean.getEMF().createEntityManager();
try{
em.getTransaction().begin();
Order order = em.find(Order.class, orderId);
em.remove(order);
em.getTransaction().commit();
}finally{
em.close();
}
}
ManagedOrderBean 和 ManagedInventoryBean 类都使用 JPA 查询。本节描述了它们如何使用 JPA 进行以下操作:
如示例 1-20 所示,ManagedInventoryBean 执行命名查询 inventoryForCategory。此类使用其 EntityManager 基于命名查询创建一个 Query 对象,上述命名查询是由 Inventory 类中的 @NamedQuery 指定的(参见示例 1-12)。之后,该类使用 Query API 设置命名参数,并利用 Query 方法 getResultList(该方法将返回满足查询的实体 Object 实例的列表)执行查询。如果您能确定某个查询只返回单个对象,那么您可以使用 Query 方法 getSingleResult 返回单个实体。
示例 1-20 在 ManagedInventoryBean 中使用命名查询
public Collection<Inventory> getInventoryForCategoryMaxQuantity(String category, int quantity){
EntityManager em = jpaResourceBean.getEMF().createEntityManager();
try{
Query query = em.createNamedQuery("inventoryForCategory");
query.setParameter("category", category);
query.setParameter("maxQuantity", quantity);
return query.getResultList();
}finally{
em.close();
}
}
有关创建命名查询的更多信息,参见指定命名查询:@NamedQuery 和 @NamedQueries。
示例 1-21 显示了 ManagedInventoryBean 如何通过指定运行时查询字符串来创建动态查询。
示例 1-21 在 ManagedInventoryBean 中使用动态查询
public Collection<Category> getCategories(){
EntityManager em = jpaResourceBean.getEMF().createEntityManager();
Collection<Category> result = em.createQuery("Select new oracle.toplink.jpa.example.inventory.nonentity.Category(i.category) from Item i group by i.category").getResultList();
em.close();
return result;
}
本节介绍了如何打包和部署教程应用程序,其中包括以下操作:
在本教程中,我们将 persistence.xml 文件打包到 <TUTORIAL_HOME>\<WAR_HOME>\WEB-INF\lib\jpa-demo.jar 中。此操作使 WEB-INF\lib 目录成为应用程序的持续性单元根目录。
您可以将 persistence.xml 文件打包到以下任意的文件中:
EJB-JAR 文件
WAR 文件
WEB-INF/classes 目录
WEB-INF/lib(在此情况下,persistence.xml 文件必须位于 JAR 文件中)
EAR
persistence.xml 文件位于 EAR 的根目录的 JAR 文件中
persistence.xml 文件位于 EAR 库目录的 JAR 文件中
应用程序客户端 JAR
EJB-JAR、WAR 或应用程序客户端 jar:
分别取决于 EJB-JAR、WAR 或应用程序 jar 的范围
对 EJB-JAR、WAR 或应用程序 jar 中定义的组件可见
应用程序的其他部分看不到此持续性单元
EAR:
取决于整个应用程序的范围
通常对应用程序中的所有组件可见
在本教程中,名为 default 的持续性单元的范围取决于教程 WAR 文件的范围:该单元仅对 WAR 文件中定义的组件可见。
要将教程应用程序打包到 Tomcat 并进行部署,请将 <TUTORIAL_HOME>\<WAR_HOME> 目录的内容压缩到名为 jpa-example.war 的文件中。
要将教程应用程序部署到 Tomcat:
验证以下系统属性的设置:
JAVA_HOME - 设置为 JDK 1.5 的安装目录。
例如:C:\Program Files\Java\jdk1.5.0_06
CATALINA_HOME - 设置为 Tomcat 的安装目录。
例如:C:\apache-tomcat-5.5.17
PATH - 您的路径必须包含 %JAVA_HOME%\bin
将 jpa-example.war 文件复制到 Tomcat CATALINA_HOME\webapps 目录中。
从命令行启动 Tomcat:
在 Windows 平台上:
cd %CATALINA_HOME% cd bin startup.cmd
在 Unix 平台上:
cd $CATALINA_HOME cd bin startup.sh
在正常退出时查找日志消息 "deployWAR INFO:Deploying web application archive jpa-example.war" 或在 CATALINA_HOME/logs/catalina.out 日志文件中查找该消息,确认应用程序的部署成功。
在运行教程应用程序前,您必须创建并填充数据库模式,如下所示:
编辑示例 1-22 中显示的命令行,使类路径包含以下内容:
<TUTORIAL_HOME>\<WAR_HOME>\WEB-INF\classes
<TUTORIAL_HOME>\<WAR_HOME>\WEB-INF\lib\jpa-demo.jar
所有 JDBC JAR
对于 Oracle 数据库,请确保将 dms.jar 包含在内。
<TUTORIAL_HOME>\<WAR_HOME>\WEB-INF\lib\toplink-essentials-agent.jar
<TUTORIAL_HOME>\<WAR_HOME>\WEB-INF\lib\toplink-essentials.jar
在此示例中,<TUTORIAL_HOME>\<WAR_HOME> 为 C:\Temp\war。
示例 1-22 利用 DDLGenerator 生成模式
java.exe -client -classpath "C:\Temp\war\WEB-INF\classes;C:\Temp\war\WEB-INF\lib\dms.jar;C:\Temp\war\WEB-INF\lib\jpa-demo.jar;C:\Temp\war\WEB-INF\lib\ocrs12.jar;C:\Temp\war\WEB-INF\lib\ojdbc14dms.jar;C:\Temp\war\WEB-INF\lib\ojdl.jar;C:\Temp\war\WEB-INF\lib\orai18n.jar;C:\Temp\war\WEB-INF\lib\toplink-essentials-agent.jar;C:\Temp\war\WEB-INF\lib\toplink-essentials.jar" -Dtoplink.ddl-generation=drop-and-create-tables oracle.toplink.jpa.example.inventory.tools.DDLGenerator
执行命令行。
编辑示例 1-23 显示的命令行,使类路径包含以下内容:
<TUTORIAL_HOME>\<WAR_HOME>\WEB-INF\classes
<TUTORIAL_HOME>\<WAR_HOME>\WEB-INF\lib\jpa-demo.jar
所有 JDBC JAR
对于 Oracle 数据库,请确保将 dms.jar 包含在内。
<TUTORIAL_HOME>\<WAR_HOME>\WEB-INF\lib\toplink-essentials-agent.jar
<TUTORIAL_HOME>\<WAR_HOME>\WEB-INF\lib\toplink-essentials.jar
在此示例中,<TUTORIAL_HOME>\<WAR_HOME> 为 C:\Temp\war。
示例 1-23 利用 Populator 填充模式
java.exe -client -classpath "C:\Temp\war\WEB-INF\classes;C:\Temp\war\WEB-INF\lib\jpa-demo.jar;C:\Temp\war\WEB-INF\lib\dms.jar;C:\Temp\war\WEB-INF\lib\ocrs12.jar;C:\Temp\war\WEB-INF\lib\ojdbc14dms.jar;C:\Temp\war\WEB-INF\lib\ojdl.jar;C:\Temp\war\WEB-INF\lib\orai18n.jar;C:\Temp\war\WEB-INF\lib\toplink-essentials-agent.jar;C:\Temp\war\WEB-INF\lib\toplink-essentials.jar" -javaagent:C:\Temp\war\WEB-INF\lib\toplink-essentials-agent.jar=main oracle.toplink.jpa.example.inventory.tools.Populator
执行命令行。
要在部署之后访问应用程序(参见第 5 步:打包与部署),请启动浏览器并输入以下 URL:
对于 Tomcat:
http://<hostname>:8080/jpa-example/index.faces
其中 <hostname> 是您部署应用程序的计算机的名称。
本教程描述了使用 JPA 管理持续性的 JSF Web 应用程序。
有关详细信息,请参见: