概述
在這個EJB 3.0學習系列中的第二部分,你將學到如何使用POJO開發數據模型,還有如何透明的將那些數據對象模型與關系型數據庫相互映射。使用EJB 3.0中注釋式的實體bean,開發數據庫驅動的應用就是小菜一碟。
請閱讀有關EJB 3.0的整個學習系列:
第一部分:使用注釋開發POJO服務
第二部分:帶來簡便的的持久化技術
在第一部分中,我討論了在企業級JavaBean 3.0(EJB)中注釋驅動的POJO編程模型。我闡述了如何開發POJO服務,如何讓容器服務使用POJO, 如何使用依賴注入來組合應用。這些POJO服務主要是用來封裝應用的商業邏輯。在商業邏輯的背後,現今的大多數應用都有由一個高性能的關系型數據庫作為支撐的數據模型層。
在第二部分中,我將討論EJB 3.0實體bean如何利用POJO和注釋的優勢來極大地簡化你的數據模型以及它們與後台關系數據庫的持久化。在我們進入EJB 3.0實體bean的細節之前,讓我們先來看一下為什麼對於企業級Java應用,數據模型和持久化是如此巨大的一個挑戰。
對象-關系映射(ORM)
在Java虛擬機中,所有的數據都被模型化並且封裝在了類和對象的樹結構中。但是在後端的關系型數據庫中,數據被模型化為關系型表,它們通過共享的鍵域(外鍵)相互關聯起來。相同的數據卻有兩個視圖,這對企業級Java的開發者來說是一個艱難的挑戰:當你想從持久化的數據存儲中存取數據時,你必須在對象與關系表達之間來回轉換,這一過程叫做對象-關系映射(ORM)。在Java EE(Java企業版,以前叫做J2EE),你可以通過兩個途徑來實現對象-關系映射。
手動的:你可以使用Java數據庫連接來直接操作持久化-對於簡單應用的直截了當的解決方案。JDBC API的類是緊貼在關系型數據庫表、行和列之後的數據模型。你必須手動地在應用的內部對象模型與JDBC對象模型之間進行轉換,如果你的應用的內部模型本身就類似於2維的關系表的話,那采用JDBC是最佳手段。
自動的:你可以把ORM交給框架。框架通常向你提供一個可以和任意數據對象進行交互的API。通過那個API,你可以存儲、獲取和查詢數據庫。框架在後台完成了框架對象的轉換。因為特定的關系型SQL查詢不適合對象接口,ORM框架通常定義它自己的查詢語言,並且自動為當前關系型數據庫生成正確的SQL語句。對於擁有復雜的數據模型的應用來說,基於框架的手段能為你節省很多時間並降低了出錯的可能。
對象數據庫
一個對象型數據庫直接在數據庫中存儲、獲取和查找對象。因為不再需要ORM,所以它對於Java應用非常適合。不幸的是,現今的對象型數據庫相對於關系型數據庫來說還不成熟,速度也慢。你可以這樣說,一個好的ORM框架從根本上來說,就是為關系型數據庫提供一個對象型數據庫的接口。兩者它都要做到最好。
這篇文章,我將重點放在專為企業級Java ORM應用設計的自動框架上。下一節,我將提到幾個流行的ORM框架和EJB 3.0中幾個關鍵的革新。
ORM 框架
EJB 實體bean是Java EE中“官方”的ORM解決方案。但是,在EJB1.x和2.x中,實體bean的難以使用是出了名的,原因如下:
●EJB 1.x和2.x實體bean必須遵守一種嚴格的組件模型。每一個bean類必須實現一個home接口和一個商業接口。它們必須從某種抽象類中繼承,而且必須實現其所有方法,即使它們多數為空。這樣的一種嚴格組件模型使得想從EJB 1.x和2.x的實體bean中構建面向對象的數據模型幾乎變得不可能了。
●EJB 1.x和2.x容器需要特別冗長的XML配置文件來建立實體bean與關系型數據庫中的表映射。那些文件是非常單調乏味和容易出錯的。
簡而言之,EJB 1.x和2.x實體bean是一個設計拙劣的ORM框架。它既沒有滿足Java數據對象模型的需求,也沒有滿足關系表數據模型的需求。出於對EJB 1.x和2.x實體bean的不滿,開發者開始尋找其它的ORM方案。實際使用中,開源的Hibernate(JBoss開發)和Oracle公司的TopLink是最成功的兩個POJO ORM框架。Hibernate和TopLink都是基於POJO的。它們不依賴於任何預定義的組件模型。作為替代,它們使用POJO數據對象(簡單的JavaBean式的),自動地解讀出如何映射它們,以及它們之間的關系(關系型數據庫)。通常,JavaBean類映射到一張數據庫表,並根據數據庫表中的外鍵映射出類之間的關系。你可以在一個簡單直接的XML配置文件中指明ORM的配置信息,比如JavaBean類對應的表名和屬性對應的列名。你可以通過框架中的工具(如:Hibernate中的Session類)來對那些POJO進行操作(如:存儲、獲取和查找)。
EJB 3.0是建立在Hibernate和TopLink的思想和成功之上。它為Java EE提供了一個標准的POJO ORM框架。另外,EJB 3.0有兩個超越現今所有持久化解決方案的關鍵革新:
●沒有使用XML文件來指明ORM配置信息, EJB 3.0允許開發者直接在POJO代碼中注釋出映射信息。舉例來說,你可以用注釋來指明每個JavaBean屬性對應的關系型表列。在這篇文章的後面,你將看到更多的例子。注釋使得映射更直接,更容易維護了。
●EJB 3.0為實體bean定義了一個新的歸檔格式。每個檔案使用一組獨立的,為後端數據庫和ORM行為所專用的配置集來定義一個持久化上下文。在這篇文章的後面,我會討論持久化上下文。
現在,讓我們通過幾個簡單的例子來看一下EJB 3.0是如何完成POJO ORM的。
映射一個簡單的對象
在EJB 3.0中,每個實體bean都是一個簡單的JavaBean式的類。為了告訴EJB 3.0容器這個類應該為持久化進行映射,你應該用@Entity來注釋這個類。
每一個實體bean類映射到一個關系型數據庫表。默認地,表名對應類名。你可以用@Table來為類指定另一個表。每一個JavaBean屬性映射到表的列上,同樣的,默認列名就是屬性名。你可以用@Column注釋在屬性的Setter方法上來改變這種默認關系。下面是一個EJB 3.0的簡單例子:
@Entity// @Table (name="AlternativeTableName")public class Person implements Serializable { protected int id; protected String name; protected Date dateOfBirth; public void setId (int id) { this.id = id; } @Id(generate = GeneratorType.AUTO) public int getId () { return id; } public void setName (String name) { this.name = name; } // @Column (name="AlternativeColumnName") public String getName () { return name; } public void setDateOfBirth (Date dateOfBirth) { this.dateOfBirth = dateOfBirth; } public Date getDateOfBirth () { return dateOfBirth; }}
當容器把Person類映射到Person SQL數據庫表以後,每一個Person實例就是表中的一條數據記錄。
映射一個簡單的JavaBean類是容易的。但自動ORM框架真正閃光之處在於映射互相關聯的對象。下一節中,我們看一下EJB 3.0如何操作對象間的關系。
關系
在一個數據模型裡面,一般來說類相互之間都會有某種聯系。比如,一個Person(個人)對象可以和一個Resume(確認)對象相關聯,反過來也一樣(一對一關系);一個Person對象可以和多個CreditCard(信用卡)對象相關,而一個CreditCard對象只能和一個Person對象相關(一對多關系)。多個Person對象可以和一個Address(地址)對象相關,而一個Person對象只能對應一個Address對象(多對一關系)。(譯者注:此處原著筆誤, Person與Address位置顛倒了;編者注:我看兩者是多對多的關系。一家人住在同一個地方,這個地址對於這一家人來說是一對多的關系;房主在別的地方又買了一套房,房主與地址的關系是一對多的關系。)
在一個數據模型中,對象指針用來操作那些關系。舉例來說,一個Person對象可以有一個屬性(也就是域)指向一個Resume對象。而另一個屬性是CreditCard對象的集合。為了告知EJB 3.0容器對象間的關系,你只需簡單地在POJO中注釋JavaBean屬性。
@Entitypublic class Person implements Serializable { // ... ... protected Resume resume; protected CreditCard [] cards; protected Address addr; // ... ... @OneToOne public Resume getResume () { return resume; } // ... ... @ManyToOne // @JoinColumn (name="MyCustomId") public Address getAddr () { return addr; } // ... ... @OneToMany public Collection getCards () { return cards; }}
在關系型數據庫中,那些關系自動地被EJB 3.0容器使用外鍵來重建了。舉例來說,Person表有一個外鍵包含了Resume表中相應的主鍵。運行時,EJB 3.0容器加強了一對一的關系:它保證了Resume鍵值對於Person表中的每一行是唯一的。為了啟用Resume表到Person表的雙向查詢,你可以Resume表中定義一個Person屬性,並把它也加上@OneToOne注釋。
Person表中也有一個外鍵包含了Address表中相應行的主鍵。這種情況下,相同的Address主鍵可以出現在多個Person行中,這是多對一關系。對於一對多的關系,映射稍有一點復雜,因為外鍵列是定義在多對一表中的。於是,在CreditCard類中,你必須用@ManyToOne來定義一個Person屬性。
改變外部鍵字段名
ORM中使用的外部鍵字段的名字是由容器自動決定的或者由@JoinColumn注釋來顯式的指定。
上面討論的關系只是實體bean之間關系的一種類型,實體類之間另外一種重要關系是繼承。
繼承
面向對象設計方法的一個關鍵概念是繼承。