前進:從EJB 2.1到EJB 3.0

來源:互聯網
上載者:User
  在開始討論怎樣從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特性,所以,在開始編寫程式之前,一定要仔細閱讀相關應用伺服器提供的文檔說明。



相關文章

Beyond APAC's No.1 Cloud

19.6% IaaS Market Share in Asia Pacific - Gartner IT Service report, 2018

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。