EJB3.0之介紹Entity(轉(zhuǎn))
EJB3介紹:Overview
EJB作為企業(yè)級(jí)的數(shù)據(jù)訪問(wèn)/持久化標(biāo)準(zhǔn)在1999年作為J2EE規(guī)范的核心規(guī)范出現(xiàn),極大的轉(zhuǎn)變了java企業(yè)級(jí)開(kāi)發(fā)的模式,為java軟件開(kāi)發(fā)提供了一個(gè)良好的架構(gòu)。 EJB從1.0到2.1在J2EE架構(gòu)中,都是作為一個(gè)服務(wù)器端的(Server side)的數(shù)據(jù)訪問(wèn)中間件。開(kāi)發(fā)人員通過(guò)EJB標(biāo)準(zhǔn)的API接口來(lái)訪問(wèn)操作數(shù)據(jù),避免直接用JDBC和Sql操作底層的數(shù)據(jù)庫(kù)。
采用EJB架構(gòu)的目標(biāo)在于:
- 減輕直接操作底層數(shù)據(jù)庫(kù)的工作量
- 為企業(yè)級(jí)開(kāi)發(fā)引入了面向?qū)ο?面向服務(wù)的開(kāi)發(fā)架構(gòu)
- 數(shù)據(jù)對(duì)象生命周期的自動(dòng)管理
- 分布式能力
- 集成/聲明式的安全/事務(wù)管理
在舊的EJB模型中(2.1以前),EJB實(shí)現(xiàn)了大部分的目標(biāo),但一個(gè)巨大的缺陷是原有的模型在試圖減輕數(shù)據(jù)訪問(wèn)工作量的同時(shí)也引入了更多的復(fù)雜開(kāi)發(fā)需求。 例如EJB核心的的Entity Bean必須特定的Home,Remote,Business接口,發(fā)布前需要預(yù)編譯,只能實(shí)現(xiàn)單表映射操作,靜態(tài)的EJB-QL(EJB查詢語(yǔ)言)都不能滿足簡(jiǎn)化數(shù)據(jù)庫(kù)操作的目標(biāo)。 EJB 2.1的復(fù)雜度,開(kāi)發(fā)成本和性能問(wèn)題使得EJB在問(wèn)世4年后仍然等不到廣泛的應(yīng)用。
到了2004年,隨著POJO( Plain Old Java Object )模型的出現(xiàn),動(dòng)態(tài)代碼操作,IOC模式等先進(jìn),簡(jiǎn)單實(shí)用技術(shù)的發(fā)展和它們?cè)诟鞣N獨(dú)立產(chǎn)品中的表現(xiàn),都證明POJO,IOC的技術(shù)比原有的EJB 2.1模型更適合作為數(shù)據(jù)訪問(wèn)中間件,開(kāi)發(fā)的難度和成本遠(yuǎn)遠(yuǎn)小于目前的EJB模型,確有更靈活和可擴(kuò)展。 2004年9月J2EE平臺(tái)規(guī)范集眾家所長(zhǎng),推出了跨越式的Java EE 5.0規(guī)范,最核心的是全面引入新的基于POJO和IOC技術(shù)的EJB3模型。到此,J2EE 5規(guī)范( Java EE 5 )成為一個(gè)集大成者,納百家之長(zhǎng),成為java企業(yè)開(kāi)發(fā)統(tǒng)一的標(biāo)準(zhǔn)規(guī)范。
EJB 3和EJB 2.1的區(qū)別
從整個(gè)EJB規(guī)范的角度來(lái)說(shuō),EJB 3和EJB 2.1最大變更在Entity Bean持久化API上。在EJB3中,Entity Bean持久化已經(jīng)單獨(dú)作為一個(gè)Persistence API規(guī)范和其他的EJB部分分離開(kāi)來(lái)。下面我們主要討論EJB 3和EJB 2.1在持久化API上的區(qū)別。
EJB 2.1模型存在復(fù)雜度高的缺陷:
- EJB 2.0 模型要求創(chuàng)建多個(gè)組件接口并實(shí)現(xiàn)多個(gè)不必要的回調(diào)方法
- 組件接口要求實(shí)現(xiàn) EJBObject 或 EJBLocalObject 以及處理許多不必要的異常
- 基于XML的EJB 2.0 部署描述符比較復(fù)雜并容易出錯(cuò)
- 基于 EJB 模型的容器管理持久性在開(kāi)發(fā)和管理方面過(guò)于復(fù)雜,并且失去了幾個(gè)基本特性--如使用數(shù)據(jù)庫(kù)序列定義主鍵的標(biāo)準(zhǔn)方法
- EJBQL 語(yǔ)法非常有限,而且是靜態(tài)的,無(wú)法做到運(yùn)行期間的動(dòng)態(tài)查詢
- EJB 2.0 組件并非是真正面向?qū)ο蟮?,因?yàn)樗鼈冊(cè)诶^承和多態(tài)性方面的有使用限制
- 開(kāi)發(fā)人員無(wú)法在 EJB 容器外部測(cè)試 EJB 模塊,而在容器內(nèi)部調(diào)試 EJB非常復(fù)雜和耗時(shí)
- 查找和調(diào)用 EJB 2.0 是一項(xiàng)復(fù)雜的任務(wù)。即使是在應(yīng)用程序中使用最基本的 EJB 也需要對(duì) JNDI 有一個(gè)詳細(xì)的了解
- 對(duì)容器的依賴使得EJB 2.0只能用于服務(wù)器組件的開(kāi)發(fā),無(wú)法實(shí)現(xiàn)一次編寫,四處運(yùn)行的面向構(gòu)件的開(kāi)發(fā)
EJB 3.0 旨在解決以往EJB 2.0 模型的復(fù)雜性和提高靈活性,具體體現(xiàn)在:
- 消除了不必要的接口Remote, Home, EJB以及回調(diào)方法實(shí)現(xiàn)
- 實(shí)體Bean采用了POJO模型,一個(gè)簡(jiǎn)單的java bean就可以是一個(gè)Entity Bean。無(wú)需依賴容器運(yùn)行和測(cè)試
- 全面采用O/R Mapping技術(shù)來(lái)實(shí)現(xiàn)數(shù)據(jù)庫(kù)操作
- 實(shí)體Bean可以運(yùn)用在所有需要持久化的應(yīng)用,不管是客戶端還是服務(wù)器端。從而真正實(shí)現(xiàn)面向構(gòu)件的開(kāi)發(fā)
- 實(shí)體 bean 現(xiàn)在支持繼承和多態(tài)性
- 靈活豐富的EJB3查詢語(yǔ)言
- SQL支持
- 使用元數(shù)據(jù)批注代替部署描述符,減少?gòu)?fù)雜配置和提高可維護(hù)性
- 將常規(guī) Java 類用作 EJB 并將常規(guī)業(yè)務(wù)接口用于 EJB
EJB 3中的元數(shù)據(jù)批注:Annotation
EJB3 規(guī)范中引入了元數(shù)據(jù)批注(Annotation)。Annotation是從J2SE 1.5開(kāi)始稱為java語(yǔ)言的一部分。Annotation并不是一個(gè)新的事物,在J2SE 1.5以前,人們已經(jīng)自行引入了象著名的XDoclet等外掛式的元數(shù)據(jù)批注方法。而在.NET中,元數(shù)據(jù)批注也早已經(jīng)是C#語(yǔ)言的成分了。
在以往,我們都是采用xml作為配置批注,但采用文本的xml配置存在一些缺陷:
- 描述符多,不容易記憶和掌握
- 無(wú)法做自動(dòng)的校驗(yàn),需要人工排錯(cuò)
- 當(dāng)系統(tǒng)變大時(shí),大量的xml配置難以管理
- 讀取和解析xml配置非常耗時(shí),導(dǎo)致應(yīng)用啟動(dòng)緩慢,不利于測(cè)試和維護(hù)
- 做O/R Mapping的時(shí)候需要在java文件和xml配置文件之間交替,增大了工作量
- 運(yùn)行中保存xml配置需要消耗額外的內(nèi)存
采用元數(shù)據(jù)可以很好的解決這些問(wèn)題:
- 描述符大量減少。以往在xml配置中往往需要描述java屬性的類型,關(guān)系等等。而元數(shù)據(jù)本身就是java語(yǔ)言,從而省略了大量的描述符
- 編譯期校驗(yàn)。錯(cuò)誤的批注在編譯期間就會(huì)報(bào)錯(cuò)。
- 元數(shù)據(jù)批注在java代碼中,避免了額外的文件維護(hù)工作
- 元數(shù)據(jù)被編譯成java bytecode,消耗小的多內(nèi)存,讀取也非常迅速,往往比xml配置解析快幾個(gè)數(shù)據(jù)量級(jí),利于測(cè)試和維護(hù)
第一個(gè)Entity Bean:HelloWorld
EJB3中的Entity Bean是如此的簡(jiǎn)單,就是一個(gè)普通的java bean加上一些精煉的元數(shù)據(jù)批注。
@Entity @Table( name="helloTable" ) public class HelloEntityBean { private int id; private String foo; /** * The entity class must have a no-arg constructor. */ public HelloEntityBean() { } public HelloEntityBean(int id, String foo) { this.id = id; this.foo = foo; } @Id(generate=GeneratorType.NONE) public int getId() { return id; } public void setId(int id) { this.id = id; } public String getFoo() { return foo; } public void setFoo(String foo) { this.foo = foo; } public String toString(){ return "HelloEntityBean: id=" + id + ", foo=" + foo; } }
代碼中元數(shù)據(jù)的說(shuō)明:
@Entity :EJB3 Entity Bean的批注,表明當(dāng)前的java bean作為一個(gè)Entity Bean來(lái)處理。
@Table( name="helloTable" ) :顧名思義,定義當(dāng)前Entity對(duì)應(yīng)數(shù)據(jù)庫(kù)中的表。
@Id(generate=GeneratorType.NONE) :我們把HelloEntityBean的id屬性定義為主鍵。主鍵產(chǎn)生策略為GeneratorType.NONE,意味這是一個(gè)業(yè)務(wù)主鍵。
就這么簡(jiǎn)單!
解說(shuō)Entity
從EJB3.0開(kāi)始,Entity Bean持久化規(guī)范與EJB的其他規(guī)范如Session Bean, Message Driven Bean和EJB3容器規(guī)范開(kāi)始分離,單獨(dú)作為一個(gè)持久化API。而在將來(lái),該持久化API很可能會(huì)從EJB3.0規(guī)范中脫離出來(lái)成為一個(gè)獨(dú)立的規(guī)范Java Persistence API。作為Java平臺(tái)上的通用數(shù)據(jù)訪問(wèn)標(biāo)準(zhǔn)接口。 為了跟規(guī)范一致,我們將在開(kāi)發(fā)手冊(cè)中將Entity Bean稱為Entity。
雖然EJB3 Entity可以是很簡(jiǎn)單的java bean,只要批注了@Entity或者在xml配置中作了說(shuō)明,就被做一個(gè)可持久化的Entity處理。 但還是需要遵行一定的規(guī)則:
- Entity類必須要有一個(gè)無(wú)參數(shù)的public或者protected的Constructor。
- 如果在應(yīng)用中需要將該Entity類分離出來(lái)在分布式環(huán)境中作為參數(shù)傳遞,該Entity Class需要實(shí)現(xiàn)java.io.Serialzable接口。
- Entity類不可以是final,也不可有final的方法。
- abstract類和Concrete實(shí)體類都可以作為Entity類。
- Entity類中的屬性變量不可以是public。Entity類的屬性必須通過(guò)getter/setter或者其他的商業(yè)方法獲得。
定義對(duì)Entity中屬性變量的訪問(wèn)
在絕大部分的商業(yè)應(yīng)用,開(kāi)發(fā)人員都可以忽略這部分無(wú)需關(guān)心。但如果你需要編寫復(fù)雜的Entity類的話,你需要了解這個(gè)部分。復(fù)雜的Entity類是指在Entity類的getter/setter和商業(yè)方法中包含比較復(fù)雜的業(yè)務(wù)邏輯而不是僅僅返回/符值某個(gè)屬性。
在大部分的情況下,我們都建議使Entity類中setter/getter中的邏輯盡可能簡(jiǎn)單,除了必要的校驗(yàn)符值外,不要包含復(fù)雜的業(yè)務(wù)邏輯,例如對(duì)關(guān)聯(lián)的其他Entity類進(jìn)行操作。但有些情況下,我們還是需要在Entity類的setter/getter方法中包含商業(yè)邏輯。這時(shí)候,采用何種屬性訪問(wèn)方式就可能會(huì)影響代碼的性能甚至是邏輯正確產(chǎn)生影響。
EJB3持久化規(guī)范中,在默認(rèn)情況下所有的屬性都會(huì)自動(dòng)的被持久化,除非屬性變量用@Transient元數(shù)據(jù)進(jìn)行了標(biāo)注。針對(duì)可持久化屬性定義了兩種屬性訪問(wèn)方式(access): FIELD和PROPERTY。
- 如果采用access=FIELD, EJB3 Persistence運(yùn)行環(huán)境(EJB3持久化產(chǎn)品,如Liberator EJB3)直接訪問(wèn)對(duì)象的屬性變量,而不是通過(guò)getter。這種訪問(wèn)方式也不要求每個(gè)屬性必須有g(shù)etter/setter。如果需要在getter中包含商業(yè)邏輯,應(yīng)該采用access=FIELD的方式。
- 如果采用access=PROPERTY, EJB3 Persistence運(yùn)行環(huán)境將通過(guò)Entity類上的getter來(lái)訪問(wèn)對(duì)象的屬性變量,這就要求每個(gè)屬性變量要有g(shù)etter/setter方法。在EJB3中,默認(rèn)的屬性訪問(wèn)方式是PROPERTY。access=PROPERTY時(shí)getter/setter的邏輯應(yīng)該盡量簡(jiǎn)單。
規(guī)范中access方式還有多一層含義。就是采用access=FIELD時(shí),元數(shù)據(jù)應(yīng)該批注在屬性上。
@Id(generate=GeneratorType.NONE) private int id; private String foo; /** * The entity class must have a no-arg constructor. */ public HelloEntityBean() { } public int getId() { return id; }
采用access=PROPERTY(默認(rèn)方式)時(shí),元數(shù)據(jù)應(yīng)該批注在對(duì)應(yīng)屬性變量的getter上。
private int id; private String foo; /** * The entity class must have a no-arg constructor. */ public HelloEntityBean() { } @Id(generate=GeneratorType.NONE) public int getId() { return id; }
為了方便開(kāi)發(fā),Liberator EJB3實(shí)現(xiàn)對(duì)元數(shù)據(jù)批注的位置比規(guī)范規(guī)定的寬松,針對(duì)屬性變量或它的getter進(jìn)行批注都可以,不受access類型的影響。
對(duì)Entity類中,getter/setter的命名規(guī)則遵從java bean規(guī)范。
Entity類中的屬性變量可以是以下數(shù)據(jù)類型:
- 原始數(shù)據(jù)類型和他們的對(duì)象類型
- java.lang.String
- java.math.BigInteger
- java.math.BigDecimal
- java.util.Date
- java.util.Calendar
- java.sql.Date
- java.sql.Time
- java.sql.Timestamp
- byte[]
- Byte[]
- char[]
- Character[]
- enums
- Entity類
- 嵌入實(shí)體類(embeddable classes)
還可以是以下集合類型:
- java.util.Collection和它的實(shí)體類
- java.util.Set和它的實(shí)體類
- java.util.List和它的實(shí)體類
- java.util.Map和它的實(shí)體類
主鍵和實(shí)體標(biāo)識(shí)(Primary Key and Entity Identity)
每個(gè)Entity類都必須有一個(gè)主鍵。在EJB3中定義了兩種主鍵:
- 鍵單主鍵
- 復(fù)合主鍵
簡(jiǎn)單主鍵必須對(duì)應(yīng)Entity中的一個(gè)屬性變量(Instance Variable),而該屬性對(duì)應(yīng)數(shù)據(jù)庫(kù)表中的一列。使用簡(jiǎn)單主鍵,我們只需要用@Id元數(shù)據(jù)對(duì)一個(gè)屬性變量或者她的getter方法進(jìn)行批注。
當(dāng)我們需要使用一個(gè)或多個(gè)屬性變量(表中的一列或多列)聯(lián)合起來(lái)作為主鍵,我們需要使用復(fù)合主鍵。復(fù)合主鍵要求我們編寫一個(gè)復(fù)合主鍵類( Composite Primary Key Class )。復(fù)合主鍵類需要符合以下一些要求:
- 復(fù)合主鍵類必須是public和具備一個(gè)沒(méi)有參數(shù)的constructor
- 復(fù)合主鍵類的每個(gè)屬性變量必須有g(shù)etter/setter,如果沒(méi)有,每個(gè)屬性變量則必須是public或者protected
- 復(fù)合主鍵類必須實(shí)現(xiàn)java.io.serializable
- 復(fù)合主鍵類必須實(shí)現(xiàn)equals()和hashcode()方法
- 復(fù)合主鍵類中的主鍵屬性變量的名字必須和對(duì)應(yīng)的Entity中主鍵屬性變量的名字相同
- 一旦主鍵值設(shè)定后,不要修改主鍵屬性變量的值
@Id private String firstName; @Id private String lastName; public Person() { }
public class PersonPK implements java.io.Serializable{ private String firstName; private String lastName; public PersonPK() { } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof PersonPK)) return false; final PersonPK personPK = (PersonPK) o; if (!firstName.equals(personPK.firstName)) return false; if (!lastName.equals(personPK.lastName)) return false; return true; } public int hashCode() { int result; result = firstName.hashCode(); result = 29 * result + lastName.hashCode(); return result; } }
posted on 2006-07-03 00:57 liaojiyong 閱讀(1143) 評(píng)論(0) 編輯 收藏 所屬分類: EJB