首页 > 开发 > 综合 > 正文

前进:从EJB 2.1到EJB 3.0

2024-07-21 02:14:40
字体:
来源:转载
供稿:网友


  在开始讨论怎样从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常用元数据注释

注释说明注释元素
@entity 注明一个实体bean类。 
@table注明实体bean表。如果未指定@table,表名与ejb名相同。name, schema
@id 注明一个主要关键属性或字段。 
@transient注明一个非持久性属性或字段。  
@column 为一个持久性实体bean属性注明一个映射栏。name、primarykey、nullable、length。默认栏名为属性或字段名。
@namedqueries 注明一组命名查询。 
@namedquery 注明一个命名查询或与查找方法相关的查询。name, querystring
@onetomany注明一个一对多联系。cascade
@onetoone注明一个一对一联系。cascade
@manytomany 注明一个多对多联系。cascade
@manytoone注明一个多对一联系。cascade

  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类方法

entitymanager方法描述
persist(object entity) 使一个实体bean实例持久化。
createnamedquery(string name)创建一个query对象的实例,以执行命名查询。
find(class entityclass, object primarykey)查找一个实体bean实例。
createquery(string ejbql) 创建一个query对象,以运行ejbql查询。
remove(object entity)移除实体bean的一个实例。

  在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特性,所以,在开始编写程序之前,一定要仔细阅读相关应用服务器提供的文档说明。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表