内容


在 WebSphere Application Server V7 中通过 Blueprint 对象注入将一个基于 JPA 的 Java EE Web 应用程序转换为 OSGi

简介

OSGi 框架是一个构建于 Java Virtual Machine (JVM) 之上的模块化动态系统,向企业应用程序开发人员提供几个关键特性,比如:

  • 生命周期管理,支持应用程序安装、更新、启动、停止和卸载,而不需要重启系统。
  • 模块化,减少较大的整体类路径上的依赖项。
  • 捆绑包版本控制,同一代码的不同版本可以共存,并可用于不同应用程序。
  • 减少 Java EE 应用程序的使用,使得多个企业应用程序包含相同的 JAR 文件。

OSGi 框架面世已近有一段时间了,但是,Java EE 企业开发人员最近才开始使用这些工具和实现来创建实际业务层面应用程序。这些工具和实现在 IBM WebSphere Application Server V7 Feature Pack for OSGi Applications and Java Persistence API (JPA) 2.0(以下称为 OSGi 功能包)中提供。Blueprint 容器提供一个依赖项注入模型,专门用于在服务可用和不可用时处理 OSGi 动态特性,也促进了 POJOs (Plain Old Java Objects) 向 OSGi 应用程序的注入。

本文旨在帮助您理解如何将您现有的 Java EE 应用程序转换成基于 OSGi 的应用程序。在此过程中,使用一个简单 Java EE Web 应用程序举例说明所需步骤。该应用程序利用 JPA 1.0 将一个实体持续化到一个数据库。步骤如下:

  • 重构清单文件。
  • 变更文件扩展名。
  • 对 EntityManager 对象进行 Blueprint 注入。
  • 事务处理。
  • Blueprint 资源引用。

如果您想使用一个新 OSGi 应用程序进行试验,这里为您提供一些 JPA 2.0 样例代码。

这里介绍的具体步骤,是假设 WebSphere Application Server V7 和 OSGi 功能包已安装,且可用于接受默认选项的开发环境。安装和配置详见 参考资料

一个简单的基于 Java EE Web 应用程序的 EAR

用于阐明步骤的样例企业应用程序,组成如下:

  • 一个 Web 应用程序(WAR 组件),其中含有一个 HTML “首页” 和一个 servlet,使用户可以在浏览器中输入一个名称字符串。servlet 调用 JAR 实用程序中的类。
  • 一个 JAR 实用程序,位于 EAR 之内,但在 WAR 之外(与 WAR 对等)。这个 JAR 实用程序含有一个执行基于 JPA 1.0 的数据库交互的类,该数据库将用户从浏览器输入的数据写入数据库表。WAR 中的 servlet 调用此类并传入输入的数据字符串。

Web 应用程序项目称为 JPA1Web。HTML 首页代码如清单 1 所示。servlet 称为 SubmitServlet_JPA1,有一个 doGet 方法,如清单 2 所示。

清单 1. HTML 首页
<HTML> 
<HEAD> 
<TITLE>Example</TITLE> 
</HEAD> 
<BODY bgcolor="WHITE"> 
<TABLE BORDER="2" CELLPADDING="2"> 
<TR><TD WIDTH="275"> 
<H2>Simple JPA Form</H2> 
Enter a name to be logged in the database.<BR> 
Clicking Submit invokes 
<A HREF="./SubmitServlet_JPA1">SubmitServlet_JPA1.java</A>,<BR>  
<FORM METHOD="POST" ACTION="/JPA1Web/SubmitServlet_JPA1"> 
<INPUT TYPE="TEXT" NAME="DATA" SIZE=30> 
<P> 
<INPUT TYPE="SUBMIT" VALUE="Click Me"> 
<INPUT TYPE="RESET"> 
</FORM> 
</TD></TR> 
</TABLE> 
</BODY> 
</HTML>
清单 2. Web 应用程序 servlet
protected void doGet(HttpServletRequest request, HttpServletResponse response) 
	throws ServletException, IOException {
		
	response.setContentType("text/html");

	PrintWriter out = response.getWriter();
		
	String DATA = request.getParameter("DATA");
		
	if (DATA!=null) {

// DBWriterImpl is the implementation class in the utility 
// JAR used for obtaining the EntityManager and invoking 
// the JPA 1.0 APIs
		
		try {
			DBWriterImpl dbWriter = new DBWriterImpl();
	
			dbWriter.setUp();
				
			dbWriter.persistName(DATA);

			out.println("\nDatabase persist complete for JPA1.");
		}
		catch (Exception e) {
			out.println("*** Exception caught in servlet: "+e.toString());
		}
	}
}

实用程序 JAR jpautility 含有需要的所有包和类,可以查找 EntityManagerFactory、获取 EntityManager 以及通过 JPA 1.0 与数据库交互。

JPA 是一个用于对象到关系的映射的行业标准,是 Enterprise JavaBeans (EJB) 3.0 规范的组成部分。此外,JPA 也支持相对容易的 POJO 持久性,它被认为在相对复杂性、性能开销以及 EJB 2.0 Entity Beans 便携性问题等方面都有所提高。

DBWriter 类(清单 3)中的样例代码查找 EntityManagerFactory,使用它来创建一个 EntityManager,然后使用用户输入的用户名来实例化一个 Customer 对象。这个 Customer 对象是一个 JPA 管理的类(@Entity),然后被持久化到数据库。

使用 Apache Maven 构建工具(见 参考资料)预构建 Customer 类,并导入到 IBM Rational® Application Developer(或其他构建环境),用于在 JAR 实用程序中进行合并。这个类和其他类作为 .class 代码驻留在 JAR 实用程序的 /ImportedClasses 目录下。

清单 3. JAR 实用程序中的 DBWriterImpl 类
package com.ibm.ws.eba.example.jpawebapp.impl;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import org.apache.openjpa.persistence.criteria.Customer;
import com.ibm.ws.eba.example.jpawebapp.DBWriter;


public class DBWriterImpl implements DBWriter {
	
	private EntityManagerFactory emf;
	private EntityManager em;

	// obtains the EMF
	public void setUp() throws Exception {
	
		try {
			emf = (EntityManagerFactory) new InitialContext().lookup
				("java:comp/env/myEntityManagerFactory");
		} catch (NamingException e) {
			System.out.println("Failed to obtain the EntityManagerFactory");
			throw new Exception(e); 
		}
	}
	
	
	// persist the Customer entity to the database
	public void persistName(String name) throws Exception {

		try {
			em = emf.createEntityManager();
		
			// resource local transactions
			em.getTransaction().begin();

    			Customer c1 = new Customer();
    			c1.setName(name);
    	 
    			em.persist(c1);
   
    			em.getTransaction().commit();
		}
		catch (Exception e) {
			throw new Exception(e);
		}
		finally {
			em.close();
    			System.out.println("Name has been written to database.");
		}
	}
}

WRA 的 web.xml 文件(清单 4)中的 persistence-unit-ref 项为 EntityManagerFactory 的查找提供了便利。JAR 实用程序中相应的 /META-INF/persistence.xml 如清单 5 所示。

清单 4. web.xml 中的持久性单元 XML 项
<persistence-unit-ref>
 <persistence-unit-ref-name>myEntityManagerFactory</persistence-unit-ref-name> 
 <persistence-unit-name>dbpersistence</persistence-unit-name> 
</persistence-unit-ref>
清单 5. 持久性 XML
<persistence xmlns=http://java.sun.com/xml/ns/persistence 
	xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance 
	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
	http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">

 	<persistence-unit name="dbpersistence" transaction-type="RESOURCE_LOCAL">
 <description>Persistence unit for the example JPA1 application</description> 
 <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
 
 <jta-data-source>jdbc/dbpersistence</jta-data-source> 
 <non-jta-data-source>jdbc/dbpersistencenojta</non-jta-data-source> 

 	<properties>
 <property name="openjpa.jdbc.SynchronizeMappings" 
			value="buildSchema(ForeignKeys=true)" /> 
	</properties>
	</persistence-unit>
</persistence>

在 WebSphere Application Server 中需要创建两个数据源,JNDI 名称分别为 jdbc/dbpersistencejdbc/dbpersistencenojta。后者是非事务型的(在管理控制台之下的 WebSphere Application Server 数据源属性链接之下选择 Non-transactional data source)。

如果数据库表和模式还没有创建,openjpa.jdbc.SynchronizeMappings 属性确保为数据实体(在这里是 Customer)自动创建正确的数据库表和模式,来持久化到数据库。

因为 JAR 实用程序位于 WAR 文件的外部,WAR 的 manifest.mf 文件包含:

Manifest-Version: 1.0
Class-Path: jpautility.jar

在应用程序构建路径上、同时可用于 Java EE 和 OSGi 应用程序的唯一外部库是 javax.j2ee.persistence.jar,可在以下路径从 OSGi 功能包安装文件中获取:

<WAS_INSTALL_ROOT>/feature_packs/jpa/plugins

这个 JAR 的旧版本接受 JPA 1.0 调用,但是上述指定版本一定要提供 JPA 2.0 类。

您可以部署这个样例应用程序作为 WebSphere Application Server 上的一个企业应用程序,在 WebSphere Application Server 中,它可以在 Web 容器中运行,使用用户输入的名称将 Customer 对象持久化到数据库。

转换成一个 OSGi 应用程序

使用基于 Java EE JAR 的应用程序本质上意味着没有机制可以用于 JAR 来声明它们的依赖性,不能包含组件版本控制、没有真正的模块化方式(在一个类路径上通常有大量类)。Java EE 应用程序通常使用供应商的库,在所有需要它们的 EAR 文件中包含这些库是很常见的。供应商库使用其他供应商的库也是很普遍的现象,这可能导致版本冲突,在一个 Java EE 应用程序中,每个类只能有一个版本。如果,您使用多个供应商的库,而这些库依赖不兼容的通用类版本,您可能会遇到麻烦。

OSGi 框架旨在克服这一缺陷。

将一个 Java EE Web 应用程序转换成一个 OSGi 应用程序也就意味着:

  • WAR 组件成为一个 Web 应用程序捆绑包(WAR)。
  • JAR 实用程序成为一个捆绑包。
  • EAR 文件成为一个企业捆绑包存档文件(EAR)。

然后,EBA 可以作为一个资产导入 WebSphere Application Server,应用程序可以作为一个业务级应用程序部署。

OSGi 框架被设计通过提供这些关键元素,来支持模块化、可重用和具有版本的应用程序的构建、部署和管理:

  • 捆绑包是有额外清单头的 JAR 组件,可以多版本并存,能够安装和运行,相互无冲突。清单属性可以指定应用程序可使用的版本。捆绑包导入和导出怎样通过一个模块层进行处理。
  • 服务注册表为捆绑包合作提供一个模型,比如,类共享和捆绑包之间的通信。服务层以一种动态的方式连接捆绑包。
  • OSGi 生命周期模型支持在运行时安装、启动、停止、更新和卸载捆绑包,不需要重启或者重引导。
  • 执行环境定义在特定平台中可用的方法和类。

为了将 JAR 实用程序转换成一个 OSGi 捆绑包,/META-INF/MANIFEST.MF 文件必须含有某些头部。对于上述样例应用程序,新捆绑包清单如清单 6 所示。

清单 6. 捆绑包 MANIFEST.MF 文件
Manifest-Version: 1.0
Bundle-SymbolicName: jpautility
Bundle-Version: 1.0.0
Bundle-ManifestVersion: 2
Meta-Persistence: 
Export-Package: com.ibm.ws.eba.example.jpawebapp;version=”1.0.0”
Import-Package: javax.persistence

在该文件中可能包含以下头部:

  • Bundle-SymbolicName:这是将要显示的名称,例如在管理控制台中,通常情况下是 JAR 的名称。这是惟一的且必需的。
  • Bundle-Version:指定给捆绑包的版本号。
  • Bundle-ManifestVersion:2 代表正在使用 Equinox Eclipse 项目(提供一个已经认证的 OSGi 核心框架规范的实现)的最新版本。这随 WebSphere Application 服务器环境提供。
  • Meta-Persistence:这个头部的存在表明这是一个持久性捆绑包,可用于指定 persistence.xml 文件的位置(如果不在默认 /META-INF 路径中)。
  • Export-Package:在 JAR 实用程序中包含的包的列表。在包的末尾显示的版本是可选的,分配一个版本号给导出的包作为这个捆绑包的一部分。
  • Import-Package:捆绑包中的类所需要的(作为导入)包的列表。不同应用程序中有所不同。包末尾的版本号是可选的,将指定能够接受的最小版本号或版本范围,导入到捆绑包中。

任何空白头部,比如上面提到的 Meta-Persistence,必须在冒号之后包含一个空格。除此之外,在一行的末尾或者最后一项下面的行中(在最后一项的末尾的回车之后)不能有额外 “空格”。

这并不是 MANIFEST.MF 文件中可用的头部的完整清单。但是在这列出的对于这个例子已经足够了。

如果 OSGi 应用程序很复杂且一个特定捆绑包存在许多版本时,Bundle-Version 将更加重要。在它们的版本表单中,例如,这些捆绑包位于 WebSphere Application Server 内部捆绑包库中(尽管没有在此显示)。一个 OSGi 应用程序可以通过一个 /META-INF/APPLICATION.MF 文件利用这些捆绑包,这个文件中可能包含在 Application-Content 头部下将要使用的捆绑包的名称和版本。指定的捆绑包版本将定义最小版本(如果版本号给定)或者可以接受的范围(使用方括号指定)。不同 OSGi 应用程序可以利用同一捆绑包的不同版本。(更多详细信息见 Apache Aries。)

Web 组件的 MANIFEST.MF 的 Class-Path: jpautility.jar 项现在可以删除。

EntityManager 的 Blueprint 注入

OSGi Service Platform Release 4 V4.2 引入了 Blueprint Container 规范。Blueprint 是 Spring 注入依赖项模型的一个标准,指定如何将一个 OSGi 应用程序的单个组件连接到一起,以及当它们在可用和不可用之间动态转换时如何处理服务。模型设计的目的是使用 POJO 支持应用程序构建,以便于它们在 OSGi 应用程序内部和外部使用。

Blueprint 和 OSGi 的整合意味着捆绑包能够发布服务,这些服务可以注入到其他捆绑包或组件中。一个 OSGi 捆绑包被视为是一个 Blueprint 捆绑包,如果它包含以下文件之一:

  • 在捆绑包默认的 /OSGI-INF/blueprint 路径下的 blueprint.xml 文件。
  • 在捆绑包中非默认路径下的 blueprint.xml 文件,在指定这个位置的 MANIFEST.MF 中附有一个 Bundle-Blueprint 头部。

任何 Blueprint 捆绑包都有一个为其创建的 Blueprint 容器(本例中,在 WebSphere Application Server 运行时内)。一个 Blueprint 扩展程序捆绑包监控框架中捆绑包的状态,如果它们是 Blueprint 捆绑包,则在它们活动时可以执行代表它们利益的活动。对应用程序有用的对象的注入就是一个例子,例如 EntityManagers。

清单 7 中的 servlet 代码执行一个 DBWriter bean 查询,而不是在 Java EE 应用程序中将常规类实例化。在这里,DBWriter 是接口类,不需要调用 DBWriter setUp 方法,因为 EntityManager 将直接注入到捆绑包中。接口类如清单 8 所示。

清单 7. OSGi 应用程序内 Servlet 的新 doGet 方法代码
if (DATA!=null) {

try {

	DBWriter dbWriter = (DBWriter) new InitialContext().lookup
		("osgi:service/com.ibm.ws.eba.example.jpawebapp.DBWriter");

		dbWriter.persistName(DATA);

		out.println("\nDatabase persist complete for JPA1.");
	}
	catch (Exception e) {
		out.println("*** Exception caught in servlet: "+e.toString());
	}
}
清单 8. DBWriter 接口类
package com.ibm.ws.eba.example.jpawebapp;

public interface DBWriter {

	public void persistName(String name);

}

当在 OSGi 框架中部署一个 OSGi 捆绑包时,beans 提供部分在框架中已注册成为服务的捆绑包(在本例中是 DBWriter bean)。查询(“osgi:service/…”)调用从 OSGi 服务注册表中动态地获取对象。

为了利用 EntityManager 的 Blueprint 注入,文件 /OSGI-INF/blueprint/blueprint.xml 在 jpautility 捆绑包中创建(以前是 Java EE 应用程序中的 JAR 实用程序)。在 Rational Application Developer 中,通过简单创建一个新源文件夹,然后创建一个新文件就可以完成了。所用 XML 文件见清单 9。

清单 9. Blueprint XML 文件
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
 xmlns:tx="http://aries.apache.org/xmlns/transactions/v1.0.0" 
	default-activation="lazy">

	<bean id="DBWriterBean" 
		class="com.ibm.ws.eba.example.jpawebapp.impl.DBWriterImpl">
	  <tx:transaction method="persistName" value="Required"/>
  	</bean>

  	<service interface="com.ibm.ws.eba.example.jpawebapp.DBWriter" 
		ref="DBWriterBean"/>

</blueprint>

blueprint.xml 文件被顶级 blueprint 元素认为是一个 Blueprint 模块,这含有名称空间声明:

xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"

这就指定文档必须符合 Blueprint 1.0.0。顶级元素也必须包含:

xmlns:tx="http://aries.apache.org/xmlns/transactions/v1.0.0" default-activation="lazy"

这个名称空间在 Blueprint 扩展中为声明式事务声明一个依赖项。该扩展由 WebSphere Application Server 中的 Blueprint 实现提供。XML 中的 default-activation=”lazy” 参数指出组件管理器根据需要激活,不是在 Blueprint 容器初始化时激活。

bean id 是一个任意标识符,以便于在这个 XML 代码中引用此类(在 Blueprint 术语中称为 “bean”)。在 <tx:transaction> 头部指定的方法 persistName 指出在 DBWriterImpl bean 上的方法和其中相关联的持久调用将作为一个容器管理的事务处理。事务方法有与其相关的一个事务性属性(值)。在本例中,该值是 Required,表示容器确保 bean 的方法总是在 JTA 中被调用,其他可能值以及关于 blueprint.xml 文件和各种组件管理器的更多信息见 参考资料

jpautility 捆绑包中的 persistence.xml 文件经修改包括一个 JTA 事务类型,而不包括 RESOURCE_LOCAL:

<persistence-unit name="dbpersistence" transaction-type="JTA">

为了完成 EntityManager 的 Blueprint 注入,@PersistenceContext 注释需要在声明之前嵌入到 bean,如清单 10 所示。

清单 10. DBWriter bean 使用 EntityManager 的 Blueprint 注入
package com.ibm.ws.eba.example.jpawebapp.impl;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.apache.openjpa.persistence.criteria.Customer;
import com.ibm.ws.eba.example.jpawebapp.DBWriter;

public class DBWriterImpl implements DBWriter {
	
	@PersistenceContext(unitName="dbpersistence")
	private EntityManager em;
	
	
	public void persistName(String name) {
				
    		Customer c1 = new Customer();
    		c1.setName(name);
    	 
    		em.persist(c1);
	}
}

EntityManager 对象的 @PersistenceContext 注释确保 Blueprint 在运行时将这个对象(准备使用)注入到捆绑包。 unitName 是 persistence.xml 文件中一个持久性单元名,便于在对像被注入到捆绑包时设置关键信息,例如用于连接 EntityManager 的数据源名称。注意到资源本地事务调用的存在,因为事务现在是容器管理的。

JTA 是一个在 Java EE 和 OSGi 中使用的标准事务 API,支持在一个 Java 环境下跨多个 XA 资源执行分布式事务。基于 JTA 的事务可以由应用程序(使用 javax.transaction.UserTransaction API)管理,或由应用程序服务器管理、也可以由事务管理器(使用 javax.transaction.TransactionManager API)管理。在这里,事务现在由 TransactionManager 管理。

在 WAR 的 web.xml 文件中,EntityManagerFactory 的 persistence-unit-ref 项现在可以删除。

修改文件扩展名

此刻,可以与 Java EE EAR 同样的方式编译应用程序并将其导出为一个 EAR。filename.ear 被修改成 filename.eba,现在可以将其作为资产导入 WebSphere Application Server,随后用于一个商业级别的应用程序;就是说,现在它是一个功能性的基于 OSGi 的应用程序。

安装应用程序

导航到 Applications > Application Types > Assets 在管理控制台导入资产,然后导航到 Applications > Application Types > Business-level applications 来创建后续业务级应用程序。

一般而言,接受导入和安装的默认设置就已经足够了,但是一个特殊的业务必须要对默认设置进行修改。(导入资产和安装业务级应用程序的相关细节,见 参考资料。)

以默认设置安装业务级应用程序和以默认设置安装企业应用程序的结果略有不同。例如,如果接受默认设置,Java EE 和 OSGi 应用程序的 URL 将类似于以下内容(假设是 9080 的一个 WC_defaulthost 以及 JPAWebApp.ear 和 JPAWebApp.eba 的应用程序名):

  • Java EE (EAR):http://hostname:9080/JPA1Web/frontPage.html
  • OSGi (EBA):http://hostname:9080/JPAWebApp.eba.JPA1Web/frontPage.html

这可能需要对 OSGi 应用程序的 frontPage.html 中的 ACTION= call 的值进行一点修改,或者在安装业务级应用程序时对 JPAWebApp.eba.JPA1Web 默认上下文路径进行修改使它们彼此一致。

如果捆绑包在多个应用程序(其中含有多个潜在开发的捆绑包版本)之间共享,那么正如之前提到的,在 WebSphere Application Server 中将 Internal Bundle Repository 看作一个可供部署和简单维护的位置是值得的。(具体 Internal Bundle Repository 超出了本文范围。)

安装完成后,实用程序组件被作为一个 OSGi 捆绑包部署,Web 组件作为一个 WAB 部署。

使用 Blueprint 资源引用

使用资源引用是获取 Java EE 和 OSGi 环境下集中管理和执行的 Java 对象的一个标准方式。一个主要的优势就是 DataSource 对象有与其相关的验证别名,来提供一定程度的安全保护,并有助于确保只允许拥有证书的应用程序访问系统资源。

为了在一个 OSGi 应用程序中使用资源引用获取数据源,首先您必须在 blueprint.xml 文件中指定它们。对于在 WebSphere Application Server JNDI 名称空间(jdbc/dbpersistence 和 jdbc/dbpersistencenojta)中定义的事务性和非事务性的数据源,您需要在 XML 中包含清单 11 所示的代码。

清单 11. 含有数据源资源引用的 Blueprint XML
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
 xmlns:rr="http://www.ibm.com/appserver/schemas/8.0/blueprint/resourcereference"
 xmlns:tx="http://aries.apache.org/xmlns/transactions/v1.0.0" 
	default-activation="lazy">

	<bean id="DBWriterBean" 
		class="com.ibm.ws.eba.example.jpawebapp.impl.DBWriterImpl">
	  <tx:transaction method="persistName" value="Required"/>
  	</bean>

  	<rr:resource-reference id="res-ref"
          interface="javax.sql.DataSource"
          filter="(osgi.jndi.service.name=jdbc/dbpersistence)">
    		<rr:res-auth>Container</rr:res-auth>
    		<rr:res-sharing-scope>Shareable</rr:res-sharing-scope>
  	</rr:resource-reference>

  	<rr:resource-reference id="res-refnojta"
          interface="javax.sql.DataSource"
          filter="(osgi.jndi.service.name=jdbc/dbpersistencenojta)">
    		<rr:res-auth>Container</rr:res-auth>
    		<rr:res-sharing-scope>Shareable</rr:res-sharing-scope>
  	</rr:resource-reference>

  	<service interface="com.ibm.ws.eba.example.jpawebapp.DBWriter" 
		ref="DBWriterBean"/>

</blueprint>

注意附加的名称空间声明 xmlns:rr。这两个数据源分别为事务型和非事务型数据源提供了“res-ref” 和 “res-refnojta” 的资源引用 ID。现在,jpautility 捆绑包的 persistence.xml 文件被修改来对这些新资源引用作出解释:

<jta-data-source>blueprint:comp/res-ref</jta-data-source>
<non-jta-data-source>blueprint:comp/res-refnojta</non-jta-data-source>

一定要注意,这个查询机制仅在使用 persistence.xml 文件的过程中受支持。在创建业务级应用程序并为其添加资产时,管理控制台中将多出一个面板,简要说明资源引用映射,使您可以链接一个验证别名到数据源来提高安全性(图 1)。

图 1. 数据源资源引用的验证别名
图 1.  数据源资源引用的验证别名
图 1. 数据源资源引用的验证别名

在应用程序中包含 JPA 2.0 代码

JPA 2.0 主要关注的是所支持的功能,这些功能是目前在各种对象关系映射供应商中提供的,而不是 JPA 1.0 中提供的。这包括对有序表、访问类型组合、多级别嵌入对象以及嵌入对象集合的支持。

JPA 2.0 规范的另一个重要因素是 Criteria query API。这个 API 用于通过创建查询定义对象来定义对检索实体及其状态的查询。Criteria 查询可移植且类型齐全,且其设计为无论使用什么数据存储都可以运行。它们提供一种方便的类似搜索的查询,其中在获得的结果中可能没有固定数量的条件。

清单 12 说明了 JPA 2.0 Criteria API (javax.persistence.criteria) 的使用。样例代码再次从 servlet 调用(但是不需要用户提供的数据)。使用了 3 个管理(@Entity)类:ustomer、Account 和 Order,其内部细节在此不详细说明,但是要说明该 API 的使用。

清单 12. DBWriter bean 经修改可使用 JPA 2.0 Criteria API
package com.ibm.ws.eba.example.jpawebapp.impl;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Fetch;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Root;

import org.apache.openjpa.persistence.criteria.Address;
import org.apache.openjpa.persistence.criteria.Customer;
import org.apache.openjpa.persistence.criteria.Order;
import com.ibm.ws.eba.example.jpawebapp.DBWriter;

public class DBWriterImpl implements DBWriter {
	
	@PersistenceContext(unitName="dbpersistence")
	private EntityManager em;
	private CriteriaBuilder cb;

	...
public void JPA2Distinct() throws Exception {
    	    
	createDataForJPA2Distinct();
	   	
	cb = em.getCriteriaBuilder();
    	
     	CriteriaQuery<Customer> cq = cb.createQuery(Customer.class);
     	Root<Customer> customer = cq.from(Customer.class);
	Fetch<Customer, Order> o = customer.fetch("orders", JoinType.LEFT);

	// define the query to get all entries where the address state
	// is either “Y”or “I”line
	cq.where(customer.get("address").get("state").in("NY", "RI"));
	// select only the distinct entries
     	cq.select(customer).distinct(true);
     	TypedQuery<Customer> distinctQuery = em.createQuery(cq);
     	distinctQuery.setMaxResults(20);
     	List<Customer> distinctResult = distinctQuery.getResultList();  

     	if (distinctResult.size()==2) System.out.println("JPA2 Criteria API
		(1) invocation complete.");
     	else throw new Exception("JPA2 Criteria API (1) invocation failed.");

	// select all entries, not just distinct ones
     	cq.distinct(false);
     	TypedQuery<Customer> indistinctQuery = em.createQuery(cq);
     	indistinctQuery.setMaxResults(20);
     	List<Customer> indistinctResult = indistinctQuery.getResultList();
        
     	if (indistinctResult.size()==3) System.out.println("JPA2 Criteria API 
		(2) invocation complete ");
     	else throw new Exception("JPA2 Criteria API (2) invocation failed.");

     	deleteDataForJPA2Distinct();
}

...
}

现在,jpautility 捆绑包的 MANIFEST.MF 文件有一个附加 Import-Package 项,可以满足 JPA 2.0 Criteria API(清单 13)。

清单 13. 捆绑包的 MANIFEST.MF 文件经修改可导入 Criteria API
Manifest-Version: 1.0
Bundle-SymbolicName: jpautility
Bundle-Version: 1.0.0
Bundle-ManifestVersion: 2
Meta-Persistence: 
Export-Package: com.ibm.ws.eba.example.jpawebapp;version=”1.0.0”
Import-Package: javax.persistence,
javax.persistence.criteria

在这个代码样例中有(使用 Criteria API 所必需的数据创建和删除的方法,见 附录 ):

  • 一个 Customer 实体,其中含有一个 Address,且一个 Address 含有一个状态。
  • 一个 Order 实体,有一个与其相关的 Customer,且一个 Customer 有许多与其相关的 Order 。
  • Customer #1 生成两个 Order,Customer #2 生成一个 Order.

所有这些实体可以持久化到数据库。应用程序是由两部分组成:

  • Criteria API 用来定义一个查询来选择 Address 的状态属性为 “NY” 或 “RI” 的所有实体。因为一位客户(其 state = NY)生成两份订单,而另一位客户(其 state = RI)生成一份订单,共计 3 个不同的实体(根据 Address )。然后查询使用这个完全不同的(ture)方法从列表中选择两个完全不同的条目。

运行这个样例可以在 WebSphere Application Server SystemOut.log 中生成以下内容:

JPA2 Criteria API (1) invocation complete.
JPA2 Criteria API (2) invocation complete.

结束语

这篇文章介绍了一个使用 JPA 1.0 调用的简单 Java EE Web 应用程序的组件部分,说明了如何将其转换成一个基于 OSGi 的应用程序。通过重构清单文件将应用程序 JAR 转换成一个捆绑包,而且生成的 OSGi 应用程序经修改可使用 Blueprint 容器来动态注入 EntityManager 和管理数据源资源引用。使用 Criteria 查询 API 示例说明 JPA 2.0 的使用。样例应用程序作为一个资产导入到 WebSphere Application Server,然后部署一个业务级应用程序,包含一个 OSGi WAB 和一个捆绑包。

附录

清单 14. 在数据库中创建和删除实体数据的 DBWriter 方法
private void createDataForJPA2Distinct() {
    	        
Address a1 = new Address(); a1.setState("NY");
      Address a2 = new Address(); a2.setState("RI");
        
      Customer c1 = new Customer(); c1.setAddress(a1);
      Customer c2 = new Customer(); c2.setAddress(a2);
        
      Order o1 = new Order(); o1.setCustomer(c1); 
      Order o2 = new Order(); o2.setCustomer(c1); 
      Order o3 = new Order(); o3.setCustomer(c2); 
        
      Set<Order> orders = new HashSet<Order>();
      orders.add(o1); orders.add(o2);
      c1.setOrders(orders);
      orders.clear();
      orders.add(o3);
      c2.setOrders(orders);
        
      em.persist(c1);
      em.persist(c2);
      em.persist(a1);
      em.persist(a2);
      em.persist(o1);
      em.persist(o2);
      em.persist(o3);
        
}


private void deleteDataForJPA2Distinct() {
    	    	
      em.createQuery("delete from Customer o").executeUpdate();
      em.createQuery("delete from Address o").executeUpdate();
      em.createQuery("delete from Order o").executeUpdate();
        
}

相关主题

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=WebSphere
ArticleID=555208
ArticleTitle=在 WebSphere Application Server V7 中通过 Blueprint 对象注入将一个基于 JPA 的 Java EE Web 应用程序转换为 OSGi
publish-date=10212010