开发人员:J2EE


将 EJB 2.0 实体 Bean 转换为 EJB 3.0

作者:Deepak Vohra

了解如何逐步将 EJB 2.0 实体 bean 转换为 EJB 3.0 实体 bean。

 

本文相关下载:
 示例文件
 Oracle 应用服务器 10g
 Oracle XDK 10g


2006 年 1 月发表

企业 JavaBeans (EJB) 3.0 规范提供了一个基于批注的 API(其中不需要远程接口/本地接口和主接口/本地主接口),从而简化了 EJB 的开发。此外,部署 EJB 3 实体 bean 时不需要作为 EJB 2.0 实体 bean 基础的部署描述符。

本教程介绍了将 EJB 2.0 实体 bean 转换为 EJB 3.0 实体 bean 的过程。

概述

Oracle 应用服务器 (10g) 10.1.3 中实现了 EJB 3.0 支持(在编写本文时是在开发人员预览版中实现的)。与 EJB 2.0 规范类相比,EJB 3.0 实体 bean 类得到了简化。

EJB 3.0 实体 bean 类是一个传统 Java 对象 (POJO),而非实现 EntityBean 接口的类。它将 EJB 2.0 中的组件接口(用于扩展 EJBLocalObject/EJBObject)和主接口(用于扩展 EJBLocalHome/EJBHome 接口)替换为 javax.persistence.EntityManager API,以创建、查找和更新实体 bean 实例。

准备安装

本教程使用 oraxsl 实用程序提供 XSLT 转换,用于将 EJB 2.0 实体 bean 转换为 EJB 3.0 规范。 下载 Oracle XML 开发人员工具包 (XDK) 10g 并将 <XDK10g>\lib\xmlparserv2.jar 添加到 Classpath。示例 EJB 2.0 实体 EJB 包含在示例 zip 文件中。

移植实体 Bean 类

EJB 3.0 实体 bean 类是一个具有 getter/setter 方法实现的非抽象 POJO 类,而 EJB 2.0 实体 bean 类是一个具有抽象 getter/setter 方法的抽象类。EJB 3.0 不需要组件接口和主接口。实体 bean 类可实现一个业务接口。

EntityManager 类用于创建、查找和更新实体 EJB。EJB 3.0 中不需要部署描述符并将其替换为元数据批注。如果包含了部署描述符,则部署描述符值将覆盖批注。使用 @Entity 批注指定一个实体 EJB。

 

使用下面列出的元数据批注指定表名、列名和主键列属性。

批注 说明 批注元素
@Table 指定用于实体 bean 持久性的表 name(如果未指定 name 元素,则将 EJB 类名用作表名)
@Column 指定一个与 EJB 属性相对应的列 name、nullable、length、updatable、primaryKey
@Id 指定一个主键列属性  
@Transient 指定一个非持久的属性  

在 EJB 2.0 中,使用 ejb-jar.xml 部署描述符中的 <query 元素<//> 指定 EJB 查询语言 (QL) 查询。在 EJB 3.0 中,使用元数据批注 @NamedQuery@NamedQueries(已在下面列出)指定 EJB QL 查询。

批注 说明 批注元素
@NamedQueries 指定一组 EJB QL 查询  
@NamedQuery 指定一个 EJB QL 查询 name="query name"
queryString="SQL query"

EJB 2.0 中的实体 bean 容器托管关系 (CMR) 关系是在 ejb-jar.xml 部署描述符中<//> 使用 <ejb-relation 元素指定的,而 EJB 3.0 中的实体 bean CMR 关系是在 bean 类中指定的。用于指定实体 bean CMR 关系的元数据批注有:

批注 说明
@OneToMany 一对多实体 bean CMR 关系
@OneToOne 一对一实体 bean CMR 关系
@ManyToOne 多对一实体 bean CMR 关系
@ManyToMany 多对多实体 bean CMR 关系

下面显示了本示例中移植到 EJB 3.0 的 EJB 2.0 实体 bean。

                               
import javax.ejb.*;
abstract public class CatalogBean implements EntityBean {
private EntityContext ctx;
public CatalogBean() {};
public void setEntityContext(EntityContext ctx) {
this.ctx = ctx;
 }

public void unsetEntityContext() {
this.ctx = null;
 }
        abstract public String getCatalogId();
        abstract public void setCatalogId(String catalogId);

        abstract public String getJournal();
        abstract public void setJournal(java.lang.String journal);

        abstract public String getPublisher();
        abstract public void setPublisher(String publisher);

public void ejbActivate() {
   }
public void ejbPassivate() {
   }
public void ejbLoad() {
   }
public void ejbStore() {
   }
public void ejbRemove()
throws RemoveException
 {
   }

public String ejbCreate(String catalogId, String journal, String publisher)
throws CreateException
 {
setCatalogId(catalogId);
setJournal(journal);
setPublisher(publisher);

return null;

public void ejbPostCreate(String catalogId, String journal, String publisher)
 {
   }
}

                            

为示例实体 bean 修改 ejb-jar.xml 部署描述符,从而包含表名、字段类型以及多实体返回值的集合类型等的元素。将 <table-name<//>、<field-type<//>和 <collection-type/> 元素添加到 ejb-jar.xml 中。下面显示了修改后的示例实体 bean 的 ejb-jar.xml 部署描述符。

                               
<?xml version="1.0"?>
<!DOCTYPE ejb-jar PUBLIC 
"-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN" 
"http://java.sun.com/dtd/ejb-jar_2_0.dtd">

<ejb-jar>
<enterprise-beans>
<entity>
<table-name>Catalog</table-name>
<ejb-name>Catalog</ejb-name>
<local-home>CatalogHome</local-home>
<local>Catalog</local>
<ejb-class>CatalogBean</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>CatalogPK</prim-key-class>
<reentrant>False</reentrant>
<cmp-version>2.x</cmp-version>
<abstract-schema-name>CatalogBean</abstract-schema-name>
<cmp-field>
<field-name>catalogId</field-name>
<field-type>String</field-type>
</cmp-field>
<cmp-field>
<field-name>journal</field-name>
<field-type>String</field-type>
</cmp-field>
<cmp-field>
<field-name>publisher</field-name>
<field-type>String</field-type>
</cmp-field>
<primkey-field>catalogId</primkey-field>
<query>
<query-method>
<method-name>findByCatalogId</method-name>
<method-params>
<method-param>java.lang.String</method-param>
</method-params>
</query-method>
<ejb-ql>
<![CDATA[SELECT OBJECT(a) FROM CatalogBean AS a WHERE a.catalogId = ?1
</ejb-ql>
    
</query>
<query>
<query-method>
<method-name>findByJournal</method-name>
<method-params>
<method-param>java.lang.String</method-param>
</method-params>
</query-method>
<ejb-ql>
<![CDATA[SELECT OBJECT(a) FROM CatalogBean AS a WHERE a.journal= ?1
</ejb-ql>
<collection-type>java.util.Collection</collection-type>
</query>
   
</entity>
</enterprise-beans>
</ejb-jar>

                            

接下来,使用 XSLT 将修改后的 ejb-jar.xml 转换为 EJB 3.0 实体 bean。

EJB 3.0 实体 bean 类由 oraxsl 实用程序生成:

                               
>oraxsl ejb-jar-modified.xml 
entity-bean.xslt CatalogBean.java

                            

用于生成 EJB 3.0 实体 bean 的 XSLT entity-bean.xslt 位于资源 zip 文件中。以下是从 EJB 2.0 实体 bean 生成的 EJB 3.0 实体 bean:

                               
import javax.persistence.Entity;

import javax.persistence.Id;
import javax.persistence.Column;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.ejb.Remote;
import javax.ejb.Local;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.ManyToOne;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

@Entity
@Table(name="Catalog")
@NamedQueries({@NamedQuery(name="FindByCatalogId",
                                

              queryString=" SELECT OBJECT(a) FROM CatalogBean AS a WHERE a.catalogId = ?1"),
@NamedQuery(name="FindByJournal",
queryString=" SELECT OBJECT(a) FROM CatalogBean AS a WHERE a.journal= ?1 ")
})

public class CatalogBean implements java.io.Serializable {
public CatalogBean(){}

public CatalogBean(
String catalogId
){

this.catalogId=catalogId;

}

private String catalogId;
private String journal;
private String publisher;

@Id
@Column(name="CATALOGID", primaryKey=true)
public String getCatalogId() {return catalogId;}
public void setCatalogId(String catalogId){this.catalogId=catalogId;}
 
public String getJournal() {return journal;}
public void setJournal(String journal){this.journal=journal;}

public String getPublisher() {return publisher;}
public void setPublisher(String publisher){this.publisher=publisher;}

}       
                              
                            

EJB 3.0 bean 类具有针对 javax.persistence 程序包类的 import 语句。 @Entity 批注将类指定为实体 EJB 类。 @Table 批注为实体 EJB 指定数据库表名, @NamedQueries 批注指定有名查询, @Id 批注指定标识符属性/主键字段, @Column 批注指定与标识符属性对应的数据库列。EJB 2.0 到 EJB 3.0 转换 XSLT 还包含实体 bean CMR 关系的转换。

开发一个用于实体 Bean 的 Session Facade

在 EJB 2.0 中,实体 bean 由 home/local home 接口中的 create 方法创建;实体 bean 字段由 local/remote 接口中的 getter/setter 方法修改。在 EJB 3.0 中,实体 bean 由 EntityManager API 创建和修改。 EntityManager 类提供了查找、持久保存和删除实体 bean 实例的方法。本部分介绍了如何生成实现 EntityManager API 的会话 EJB。

在会话 EJB bean 类中,使用 @Resource 批注获取 EntityManager。

                               
@Resource private EntityManager em;

                            

下面列出了 EntityManager 类一些常用的方法。

EntityManager 方法 说明
persist(Object entity) 在数据库中保存一个实体 bean 实例。persist 方法返回在数据库中持久保存的实体 bean。
find(String entityName, Object primaryKey) 查找具有某个主键的实体 bean 实例。
remove(Object entityBean) 从数据库中删除实体 bean。
createQuery(String ejbQlString) 创建一个 EJB QL 查询。
createNamedQuery(String queryName) 创建一个 NamedQuery 查询。

在会话 bean 类中,使用 create 方法创建实体 bean。例如,与标识符属性 catalogId 对应的 create 方法为

                               
public void create(String catalogId)
{   CatalogBean catalogBean = new CatalogBean(catalogId);
em.persist(catalogBean);
 }

                            

EntityManager 类的 persist 方法在数据库中保存一个新的实体 EJB。

EntityManager 类的 remove 方法用于删除一个 Entity bean。

                               
public void remove(CatalogBean  catalogBean) {
em.remove(catalogBean);
  }

                            

EntityManager 类的 find 方法用于查找实体 bean。例如,与标识符属性 catalogId(使用 @Id 批注在实体 bean 类中定义)对应的 findByPrimaryKey 方法为

                               
public CatalogBean findByPrimaryKey(String catalogId) 
{   return (CatalogBean)em.find("CatalogBean" , catalogId);
 }

                            

在会话 EJB 中,为在 EJB bean 类中定义的有名查询添加 finder 方法。 createNamedQuery 方法用于获取有名查询的查询对象。例如,与有名查询 FindByCatalogId(在实体 bean 类中定义)对应的 finder 方法为

                               
public CatalogBean findByCatalogId(java.lang.String param1)  
{Query query=em.createNamedQuery("FindByCatalogId");
query.setParameter(0, param1);
return (CatalogBean)(query.getSingleResult());
 }

                            

findByCatalogId 方法中,从有名查询 FindByCatalogId 中获取 javax.persistence.Query 对象。参数值在查询对象上设置。使用查询对象的 getSingleResult 方法获取一个实体 EJB bean 实例。有名查询 FindByCatalogId 返回实体 EJB bean 的一个实体。

有名查询还返回一个实体集合。例如,有名查询 FindByJournal 返回一个集合。与 FindByJournal 有名查询对应的 finder 方法为

                               
public java.util.List<CatalogBean> findByJournal(java.lang.String param1)
{   Query query= em.createNamedQuery("FindByJournal");
query.setParameter(0, param1);
return query.getResultList();
}

                            

findByJournal 方法中,从有名查询中获取一个查询对象并使用该查询对象的 getResultList 方法获取一个实体 bean 的 java.util.List。

然后,使用 XSLT 生成实现 EntityManager API 的 EJB 3.0 会话 bean 类:

                               
> oraxsl ejb-jar-modified.xml 
facade-bean.xslt CatalogFacadeBean.java

                            

EJB 3.0 会话 bean 类实现 EJB 3.0 会话 bean 远程接口 CatalogFacade。下面显示了生成的会话 bean 类。

                               
import javax.persistence.EntityManager;
import javax.ejb.Resource;
import javax.ejb.Stateless;
import javax.persistence.Query;

@Stateless
                
public class CatalogFacadeBean implements CatalogFacade{

@Resource
private EntityManager em;
private CatalogBean catalog;

public void create(String catalogId) {
CatalogBean catalogBean = new CatalogBean(catalogId);
em.persist(catalogBean);
 }

public void remove(CatalogBean catalogBean) {
em.remove(catalogBean);
 }

public CatalogBean findByPrimaryKey(String catalogId) {
return (CatalogBean)em.find("CatalogBean", catalogId);
 }


public CatalogBean findByCatalogId(java.lang.String param1) {
  
Query query=em.createNamedQuery("FindByCatalogId");


query.setParameter(0, param1);

return (CatalogBean)(query.getSingleResult());
 }

public java.util.List<CatalogBean> findByJournal(java.lang.String param1){
    
Query query= em.createNamedQuery("FindByJournal");
query.setParameter(0, param1);
return query.getResultList();}


}

                            

@Stateless 批注指定一个无状态会话 EJB。使用 XSLT facade.xslt 生成会话 bean 接口。

                               
> oraxsl ejb-jar-modified.xml 
facade.xslt CatalogFacade.java

                            

以下是会话 bean 接口:

                               
import javax.ejb.Remote;
import javax.ejb.Remove;

@Remote
                
public interface CatalogFacade{

void create(String catalogId); 


void remove(CatalogBean catalogBean); 

CatalogBean findByPrimaryKey(String catalogId); 

CatalogBean findByCatalogId(java.lang.String param1); 

java.util.List<CatalogBean> findByJournal(java.lang.String param1); 

}

                            

@Remote 批注指定远程接口。示例 zip 文件中提供了 XSLT facade-bean.xslt(用于生成会话 bean 类)和 XSLT facade.xslt(会话 bean 接口)。

结论

可以使用 XSLT 转换将使用先前版本的 EJB 规范开发的实体 EJB 转换为 EJB 3.0 规范。可以使用示例 XSLT 转换转换 EJB 2.0 和 EJB 2.1。

可以使用 如何使用 EJB 3.0 开发实体 Bean 教程中介绍的过程在 Oracle 应用服务器中部署通过转换生成的实体 bean。

 


Deepak Vohra ( dvohra09@yahoo.com) 是 Web 开发人员、NuBean 顾问并且是 Sun 认证的 Java 程序员。

 

将您的意见发送给我们