站内搜索: 请输入搜索关键词

当前页面: 开发资料首页JSP 专题前进:从EJB 2.1到EJB 3.0

前进:从EJB 2.1到EJB 3.0

摘要: 前进:从EJB 2.1到EJB 3.0
<tr> <td>   在开始讨论怎样从EJB 2.1迁移到EJB 3.0之前,有必要先了解一下迁移之后将会得到什么:主要来说,EJB 3.0减少了在创建EJB时所需的类、接口、部署描述符的数量。EJB 3.0通过用纯旧式Java对象(POJO)取代抽象bean类,用纯旧式Java接口(POJI)取代组件与主接口(Component & Home),简化了EJB的开发过程,在此,后者是可选项--你不必全部包含进它们。

  部署描述符--ejb-jar.xml--由其指定了EJB名、bean对象名、接口、查找者方法、容器管理关系(CMR),在此就不再需要其他与开发商相关的部署描述符了,因为已被组件类中的元数据注释所取代。这就是你为什么需要使用JDK 5.0来开发EJB 3.0应用的原因,因为它们使用了注释,而注释在JDK 5.0之前不可用。

  EJB 3.0用javax.persistence.EntityManager API取代了EJB 2.1中的查找者方法,通常EJB 2.1的客户端应用使用JNDI名来获得一个对实体(entity)及会话(session)bean对象的引用,而EJB 3.0客户端应用则是使用@Resource、@Inject和@EJB。

  在EJB 2.1中,可使用javax.ejb包装类与接口来开发实体与会话,在此,一个会话bean实现了SessionBean接口,而一个实体bean实现了EntityBean接口;相比之下,EJB 3.0的会话与实体bean类是POJO,并没有实现SessionBean和EntityBean接口。

  一个EJB 2.1的会话bean类指定了一个或多个ejbCreate方法、回调方法、setSessionContext方法和业务(business)方法;与此类似,一个EJB 2.1实体指定了ejbCreate()、ejbPostCreate()、回调、容量管理持久性(CMP)、CMR的getter/setter和业务方法。一个EJB 3.0会话bean类只指定了业务方法;同样地,一个EJB 3.0实体bean只指定了业务方法、对不同bean属性的getter/setter方法及对bean关系的getter/setter方法。

  EJB 2.1主接口扩展了javax.ejb.EJBHome接口、另有一个本地主接口扩展了javax.ejb.EJBLocalHome接口;EJB 2.1的远程接口扩展了javax.ejb.EJBObject接口,另有一个本地接口扩展了javax.ejb.EJBLocalObject接口。在EJB 3.0中,并没有指定组件与主接口--它们已被POJI取代,如果一个会话bean类没有指定一个业务接口,那么EJB服务器将从会话bean类中为它生成一个POJI业务接口。

  请在脑海中记住这些变化,本文的后续部分,将用两个示例来集中讲述把一个会话bean和一个实体bean,从EJB 2.1迁移到EJB 3.0时所需的详细信息。

  迁移会话bean

  示例中的EJB 2.1会话bean类--BookCatalogBean--指定了一个ejbCreate方法、一个称为getTitle()的业务方法和一个回调方法:

// BookCatalogBean.java
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;

public class BookCatalogBean implements SessionBean
{
 private SessionContext ctx;
 public String getEdition(String title)
 {
  if(title.equals("Java & XML"))
   return new String("第二个版本");
  if(title.equals("Java and XSLT"))
   return new String("第一个版本");
 }
 public void ejbCreate(){}
 public void ejbRemove() {}
 public void ejbActivate() {}
 public void ejbPassivate() {}
 public void setSessionContext(SessionContext ctx)
 {this.ctx=ctx;}
}
  在EJB 3.0会话bean中,可使用元数据注释来指定bean类型,即使用@Stateful和@Stateless来分别指定Stateful(有状态)或Stateless(无状态)。也可在一个会话bean中用一个业务接口来取代组件与主接口,因为业务接口是一个POJI,所以可用@Local和@Remote来指定其为本地或远程类型,而一个会话bean可同时实现本地与远程接口。

  如果在bean类不指定接口类型(本地或远程),那EJB服务器在默认情况下会自动生成一个本地业务接口,在此也可使用@Local和@Remote注释来指定接口类。

  下面的EJB 3.0会话bean是一个POJO,其由前面的BookCatalogBean.java EJB 2.1无状态会话bean移植而来,注意它使用了@Stateless注释,实现了一个本地业务接口,并在@Local注释中指定了本地接口类名。

// BookCatalogBean.java EJB 3.0 Session Bean
@Stateless
@Local ({BookCatalogLocal.java})
public class BookCatalogBean implements
BookCatalogLocal
{
 public String getEdition(String title)
 {
  if(title.equals("Java & XML"))
   return new String("第二个版本");
  if(title.equals("Java and XSLT"))
   return new String("第一个版本");
 }
}


  另外,也要注意,通过@Local注释,上面的EJB 3.0bean类用一个本地业务接口(POJI)取代了EJB 2.1中的组件与主接口。

  迁移EJB会话bean客户端

  一个EJB 2.1会话bean的客户端通过使用JNDI名可取得一个会话bean对象,如下所示的客户端使用了BookCatalogLocalHome的JNDI名取得一个本地主对象,接着调用了create()方法,随后,客户端用getEdition(String)业务方法输出特定标题的版本值。

import javax.naming.InitialContext;
public class BookCatalogClient
{
 public static void main(String[] argv)
 {
  try{
   InitialContext ctx=new InitialContext();
   Object objref=ctx.lookup("BookCatalogLocalHome");
   BookCatalogLocalHome catalogLocalHome = (BookCatalogLocalHome)objref;
   BookCatalogLocal catalogLocal = (BookCatalogLocal) catalogLocalHome.
   create();
   String title="Java and XML";
   String edition = catalogLocal.getEdition(title);
   System.out.println("标题的版本:" + title + " " + edition);
  }
  catch(Exception e){}
 }
}
  在EJB 3.0中,可通过依赖性注入,来获取一个对会话bean对象的引用,这通常由@Inject、@Resource、@EJB注释来实现。如下所示的EJB 3.0会话bean客户端使用了@Inject注释注入到BookCatalogBean类中,仍可由getEdition(String)业务方法来获取标题的版本值。

public class BookCatalogClient
{
 @Inject BookCatalogBean;
 BookCatalogBean catalogBean;

 String title="Java and XML";
 String edition=catalogBean.getEdition(edition);
 System.out.println("标题版本:" + title + " " + edition);
}
  迁移实体bean

  本节讲述如何迁移EJB 2.1的实体bean到EJB 3.0。一个EJB 2.1实体bean实现了EntityBean接口,其由getter和setter CMP字段方法、getter和setter CMR字段方法、回调方法及ejbCreate/ejbPostCreate方法组成。示例实体bean(见例1)--BookCatalogBean.java,由CMP字段标题、作者、发行者和CMR字段版本组成。

  例1:BookCatalogBean.java

import javax.ejb.EntityBean;
import javax.ejb.EntityContext;

public class BookCatalogBean implements EntityBean
{
 private EntityContext ctx;
 public abstract void setTitle();
 public abstract String getTitle();
 public abstract void setAuthor();
 public abstract String getAuthor();
 public abstract void setPublisher();
 public abstract String getPublisher();
 public abstract void setEditions(java.util.Collection editions);
 public abstract java.util.Collection getEditions();

 public String ejbCreate(String title)
 {
  setTitle(title);
  return null;
 }

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

 public void setEntityContext(EntityContext ctx)
 {
  this.ctx=ctx;
 }

 public void unsetEntityContext()
 {
  ctx = null;
 }
}
  而这个EJB 2.1实体bean的ejb-jar.xml部署描述符(见例2)文件,指定了EJB类、接口、CMP字段、EJB QL查询和CMR关系。BookCatalogBean实体Bean定义了一个查找方法findByTitle()、一个CMR字段及版本。

  例2: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>
   <ejb-name>BookCatalog</ejb-name>
   <local-home>BookCatalogLocalHome</local-home>
   <local>BookCatalogLocal</local>
   <ejb-class>BookCatalogBean</ejb-class>
   <persistence-type>Container</persistence-type>
   <prim-key-class>String</prim-key-class>
   <reentrant>False</reentrant>
   <cmp-version>2.x</cmp-version>
   <abstract-schema-name>BookCatalog</abstract-schema-name>
   <cmp-field>
    <field-name>title</field-name>
   </cmp-field>
   <cmp-field>
    <field-name>author</field-name>
   </cmp-field>
   <cmp-field>
    <field-name>publisher</field-name>
   </cmp-field>
   <query>
    <query-method>
     <method-name>findByTitle</method-name>
     <method-params>
      <method-param>java.lang.String</method-param>
     </method-params>
    </query-method>
    <ejb-ql>
     <![CDATA[SELECT DISTINCT OBJECT(obj) FROM BookCatalog obj WHERE obj.title = ?1 ]]>
    </ejb-ql>
   </query>
  </entity>
</enterprise-beans>
<relationships>
 <ejb-relation>
  <ejb-relation-name>BookCatalog-Editions</ejb-relation-name>
  <ejb-relationship-role>
   <ejb-relationship-role-name>
    BookCatalog-Has-Editions
   </ejb-relationship-role-name>
   <multiplicity>One</multiplicity>
   <relationship-role-source>
    <ejb-name>BookCatalog</ejb-name>
   </relationship-role-source>
   <cmr-field>
    <cmr-field-name>editions</cmr-field-name>
    <cmr-field-type>java.util.Collection</cmr-field-type>
   </cmr-field>
  </ejb-relationship-role>
  <ejb-relationship-role>
   <ejb-relationship-role-name>
    Editions-Belong-To-BookCatalog
   </ejb-relationship-role-name>
   <multiplicity>One</multiplicity>
   <cascade-delete />
   <relationship-role-source>
    <ejb-name>Edition</ejb-name>
   </relationship-role-source>
  </ejb-relationship-role>
 </ejb-relation>
</relationships>
</ejb-jar>

  相比之下,对应于EJB 2.1实体bean类的EJB 3.0实体Bean类是一个纯旧式Java对象(POJO),并且非常简单(请看例3)。此bean类的EJB 3.0版本使用了元数据注释@Entity,而EJB 2.1部署描述符ejb-jar.xml文件中用元素符指定的查找方法,在EJB 3.0 Bean类中,则使用@NamedQueries和@NamedQuery注释来指定;ejb-jar.xml文件中用元素符指定的CMR关系,在EJB 3.0 Bean类中,则用元数据注释来指定;另外,主要的关键字段通过@Id注释来指定。表1中列出了一些EJB 3.0的元数据注释。

  例3:BookCatalogBean.java

import javax.persistence.Entity;
import javax.persistence.NamedQuery;
import javax.persistence.Id;
import javax.persistence.Column;
import javax.persistence.OneToMany;

@Entity
@NamedQuery(name="findByTitle", queryString =
"SELECT DISTINCT OBJECT(obj) FROM BookCatalog obj WHERE obj.title = ?1")

public class BookCatalogBean
{
 public BookCatalogBean(){}
 public BookCatalogBean(String title)
 {
  this.title=title;
 }

 private String title;
 private String author;
 private String publisher;

 @Id
 @Column(name="title", primaryKey="true")

 public String getTitle(){return title;}
 public void setTitle(){this.title=title;}
 public void setAuthor(String author){this.author=author;}
 public String getAuthor(){return author;}
 public void setPublisher(String publisher)
 {
  this.publisher=publisher;
 }
 public String getPublisher(){return publisher;}
 private java.util.Collection<Edition>editions;

 @OneToMany
 public void setEditions(java.util.Collection editions)
 {
  this.editions=editions;
 }

 public java.util.Collection getEditions(){return editions;}
}
  表1:EJB 3.0常用元数据注释

<table cellSpacing=0 cellPadding=0 width="90%" align=center border=1> <tr> <td width="9%">注释</td> <td width="50%">说明</td> <td width="41%">注释元素</td></tr> <tr> <td width="9%">@Entity </td> <td width="50%">注明一个实体bean类。</td> <td width="41%"> </td></tr> <tr> <td width="9%">@Table</td> <td width="50%">注明实体bean表。如果未指定@Table,表名与EJB名相同。</td> <td width="41%">name, schema</td></tr> <tr> <td width="9%">@Id </td> <td width="50%">注明一个主要关键属性或字段。</td> <td width="41%"> </td></tr> <tr> <td width="9%" height=22>@Transient</td> <td width="50%" height=22>注明一个非持久性属性或字段。 </td> <td width="41%" height=22> </td></tr> <tr> <td width="9%">@Column </td> <td width="50%">为一个持久性实体bean属性注明一个映射栏。</td> <td width="41%">Name、primaryKey、nullable、length。默认栏名为属性或字段名。 </td></tr> <tr> <td width="9%">@NamedQueries </td> <td width="50%">注明一组命名查询。</td> <td width="41%"> </td></tr> <tr> <td width="9%">@NamedQuery </td> <td width="50%">注明一个命名查询或与查找方法相关的查询。</td> <td width="41%">name, queryString</td></tr> <tr> <td width="9%">@OneToMany</td> <td width="50%">注明一个一对多联系。</td> <td width="41%">Cascade</td></tr> <tr> <td width="9%">@OneToOne</td> <td width="50%">注明一个一对一联系。</td> <td width="41%">Cascade</td></tr> <tr> <td width="9%">@ManyToMany </td> <td width="50%">注明一个多对多联系。</td> <td width="41%">Cascade</td></tr> <tr> <td width="9%">@ManyToOne</td> <td width="50%">注明一个多对一联系。</td> <td width="41%">Cascade</td></tr></table>
  EJB 2.1 bean类中的查找方法findByTitle(),在EJB 3.0中则使用相应的@namedQuery注释;EJB 2.1实体bean中的CMR关系,在EJB 3.0实体bean中则使用@OnetoMany注释。注释@Id注明了标识符属性标题,注释@Column指定了与标识符属性标题对应的数据库栏。如果一个持久性实体bean属性未用@Column注明,那EJB服务器会假定栏名与实体bean属性名相同。而瞬态实体bean属性通常用@Transient来注明。

  迁移EJB实体Bean客户端

  你可在实体bean主接口或本地主接口中使用create()方法,来创建一个EJB 2.1实体bean主对象或本地主对象。通常,一个EJB 2.1实体bean的客户端可通过JNDI查找来获取一个实体bean的本地或远程对象。下面有一段示例代码,其创建了一个EJB 2.1实体bean的本地主对象。

InitialContext ctx=new InitialContext();
Object objref=ctx.lookup("BookCatalogLocalHome");
BookCatalogLocalHome catalogLocalHome = (BookCatalogLocalHome)objref;
  在上面的代码段中,BookCatalogLocalHome是BookCatalogBean实体bean的JNDI名。

  在得到一个引用之后,EJB 2.1的客户端通过create()方法创建了一个本地对象。

BookCatalogLocal catalogLocal = (BookCatalogLocal)
catalogLocalHome.create(title);
  在EJB 2.1中,可通过查找方法,从一个本地主对象中取得一个本地或远程对象。例如,你可像如下所示通过findByPrimaryKey方法取得一个本地对象。

BookCatalogLocal catalogLocal = (BookCatalogLocal)
catalogLocalHome.findByPrimaryKey(title);
  另外在EJB 2.1中,可使用remove()方法移除一个实体bean的实例:

catalogLocal.remove();
  EJB 3.0通过javax.persistence.EntityManager类实现了持久性、查找和移除。表2列出了EntityManager类中用于取代EJB 2.1方法的一些常用方法。

  表2:EntityManager类方法

<table cellSpacing=0 cellPadding=0 width="90%" align=center border=1> <tr> <td>EntityManager方法</td> <td>描述</td></tr> <tr> <td>persist(Object entity) </td> <td>使一个实体bean实例持久化。</td></tr> <tr> <td>createNamedQuery(String name)</td> <td>创建一个Query对象的实例,以执行命名查询。</td></tr> <tr> <td>find(Class entityClass, Object primaryKey)</td> <td>查找一个实体bean实例。</td></tr> <tr> <td>createQuery(String ejbQl) </td> <td>创建一个Query对象,以运行EJBQL查询。</td></tr> <tr> <td>remove(Object entity)</td> <td>移除实体bean的一个实例。</td></tr></table>
  在EJB 3.0实体bean的客户类中,可使用@Resource注释来注入EntityManager对象。

@Resource
private EntityManager em;
  可调用EntityManager.persist()方法来使一个实体bean的实例持久化,例如:

BookCatalogBean catalogBean = new BookCatalogBean (title);
em.persist(catalogBean);
  类似地,可调用EntityManager.find()方法来取得一个实体bean的实例。
 
BookCatalogBean catalogBean = (BookCatalogBean)
em.find("BookCatalogBean", title);
  在此还可以定义一个相当于命名查询findByTitle的EJB 3.0客户类查找方法(与EJB 2.1中的查找方法可不一样),用createNamedQuery(String)方法取得一个Query对象。

Query query=em.createNamedQuery("findByTitle");
  通过setParameter(int paramPosition, String paramValue)或setParameter(String parameterName, String value)方法设置Query对象的参数,注意此处的参数位置是从0开始的。

query.setParameter(0, title);
  使用Query.getResultList()方法取得BookCatalogBean对象的一个集合,如果确定查询只返回一个单一的结果,还可以使用getSingleResult()方法代替。

java.util.Collection catalogBeanCollection = (BookCatalogBean)query.getResultList();
  最后,用EntityManager.remove(Object entity)方法移除实体bean的实例。

BookCatalogBean catalogBean;
em.remove(catalogBean);
  例4演示了一个完整的EJB 3.0实体bean的无状态会话bean客户类。

  例4:BookCatalogClient类

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

@Stateless
@Local
public class BookCatalogClient implements BookCatalogLocal
{
 @Resource
 private EntityManager em;

 public void create(String title)
 {
  BookCatalogBean catalogBean=new BookCatalogBean(title);
  em.persist(catalogBean);
 }
 public BookCatalogBean findByPrimaryKey(String title)
 {
  return (BookCatalogBean)em.find("BookCatalogBean", title);
 }

 public java.util.Collection findByTitle(String title)
 {
  Query query=em.createNamedQuery("findByTitle");
  query.setParameter(0, title);
  return (BookCatalogBean)query.getResultList();
 }

 public void remove(BookCatalogBean catalogBean)
 {
  em.remove(catalogBean);
 }
}
  以上的示例演示了如何把一个会话bean和实体bean从EJB 2.1迁移到EJB 3.0,从EJB 2.0迁移的情况也与此类似。

  在本文完稿时,已有一些应用服务器支持EJB 3.0规范,如JBoss应用服务器、Oracle应用服务器及Caucho应用服务器。不幸的是,这些应用服务器对EJB 3.0的实现会有所不同----它们可能没有实现全部的EJB 3.0特性,所以,在开始编写程序之前,一定要仔细阅读相关应用服务器提供的文档说明。

</td> </tr> </table>
↑返回目录
前一篇: IIS与Apache共用80端口方法
后一篇: J2ME中多线程网络连接编程的分析