1.JPA概述
JPA(Java Persistence API)作為Java EE 5.0平臺標準的ORM規範,將得到所有Java EE伺服器的支援。
Sun這次吸取了之前EJB規範慘痛失敗的經歷,在充分吸收現有ORM框架的基礎上,得到了一個易於使用、伸縮性強的ORM規範。
從目前的開發社群的反應上看,JPA受到了極大的支援和讚揚,JPA作為ORM領域標準化整合者的目標應該不難實現。
JPA透過JDK 5.0註解或XML描述物件-關係表的對映關係,並將執行期的實體物件持久化到資料庫中。
Sun引入新的JPA ORM規範出於兩個原因:
其一,簡化現有Java EE和Java SE應用的物件持久化的開發工作;
其二,Sun希望整合對ORM技術,實現天下歸一。
JPA由EJB 3.0軟體專家組開發,作為JSR-220實現的一部分。
但它不囿於EJB 3.0,你可以在Web應用、甚至桌面應用中使用。
JPA的宗旨是為POJO提供持久化標準規範,由此可見,經過這幾年的實踐探索,能夠脫離容器獨立執行,方便開發和測試的理念已經深入人心了。
目前Hibernate 3.2、TopLink 10.1.3以及OpenJpa都提供了JPA的實現。
JPA的總體思想和現有Hibernate、TopLink,JDO等ORM框架大體一致。
總的來說,JPA包括以下3方面的技術:
1、ORM對映元資料,JPA支援XML和JDK 5.0註解兩種元資料的形式,元資料描述物件和表之間的對映關係,框架據此將實體物件持久化到資料庫表中;
2、JPA 的API,用來操作實體物件,執行CRUD操作,框架在後臺替我們完成所有的事情,開發者從繁瑣的JDBC和SQL程式碼中解脫出來。
3、查詢語言,這是持久化操作中很重要的一個方面,透過面向物件而非面向資料庫的查詢語言查詢資料,避免程式的SQL語句緊密耦合。
2、實體物件
訪問資料庫前,我們總是要設計在應用層承載資料的領域物件(Domain Object),ORM框架將它們持久化到資料庫表中。
為了方便後面的講解,我們用論壇應用為例,建立領域物件:
Topic -----(extends)----> PollTopic 1--------------*> PollOption
Topic是論壇的主題,而PollTopic是調查性質的論壇主題,擴充套件於Topic,一個調查主題擁有多個選項PollOption。
這三個領域物件很好地展現了領域物件之間繼承和關聯這兩大核心的關係。這3個領域物件將被對映到資料庫的兩張表中:
T_topic程式碼
topic_id id <pk>
topic_title varchar(100)
topic_time datetime
topic_views int
topic_type tinyint
multiple tinyint
max_choice tinyint
T_polloption程式碼
option_id int <pk>
topic_id int
votes int
displayorder tinyint
option varchar(80)
其中,Topic及其子類PollTopic將對映到同一張t_topic表中,並用topic_type欄位區分兩者。而PollOption對映到t_polloption中。
具有ORM元資料的領域物件稱為實體(Entity),按JPA的規範,實體具備以下的條件:
1、必須使用javax.persistence.Entity註解或者在XML對映檔案中有對應的元素;
2、必須具有一個不帶參的建構函式,類不能宣告為final,方法和需要持久化的屬性也不能宣告為final;
3、如果遊離狀的實體物件需要以值的方式進行傳遞,如通Session bean的遠端業務介面傳遞,則必須實現Serializable介面;
4、需要持久化的屬性,其訪問修飾符不能是public,它們必須透過實體類方法進行訪問。
3.使用註解元資料
@Entity:
將領域物件標註為一個實體,表示需要儲存到資料庫中,預設情況下類名即為表名,透過name屬性顯式指定表名,如name = "T_TOPIC",表示Topic儲存到T_TOPIC表中;
@Id :
對應的屬性是表的主鍵;
@GeneratedValue:
主鍵的產生策略,透過strategy屬性指定。預設情況下,JPA自動選擇一個最適合底層資料庫的主鍵生成策略:
如SqlServer對應identity,MySql對應auto increment。
在javax.persistence.GenerationType中定義了以下幾種可供選擇的策略:
1) IDENTITY:表自增鍵欄位,Oracle不支援這種方式;
2) AUTO: JPA自動選擇合適的策略,是預設選項;
3) SEQUENCE:透過序列產生主鍵,透過@SequenceGenerator註解指定序列名,MySql不支援這種方式;
4) TABLE:透過表產生主鍵,框架藉由表模擬序列產生主鍵,使用該策略可以使應用更易於資料庫移植。
不同的JPA實現商生成的表名是不同的:
如 OpenJPA生成openjpa_sequence_table表
Hibernate生成一個hibernate_sequences表,
而TopLink則生成sequence表。這些表都具有一個序列名和對應值兩個欄位,如SEQ_NAME和SEQ_COUNT。
@Column(name = "TOPIC_ID"):
屬性對應的表字段。我們並不需要指定表字段的型別,因為JPA會根據反射從實體屬性中獲取型別;如果是字串型別,我們可以指定欄位長度,以便可以自動生成DDL語句;
@Temporal(TemporalType.DATE):
如果屬性是時間型別,因為資料表對時間型別有更嚴格的劃分,所以必須指定具體時間型別。在javax.persistence.TemporalType列舉中定義了3種時間型別:
1) DATE :等於java.sql.Date
2) TIME :等於java.sql.Time
3) TIMESTAMP :等於java.sql.Timestamp
繼承關係
Topic和PollTopic是父子類,JPA 採用多種方法來支援實體繼承。在父類中必須宣告繼承實體的對映策略。
對於繼承的實體,在javax.persistence.InheritanceType定義了3種對映策略:
SINGLE_TABLE:
父子類都儲存到同一個表中,透過欄位值進行區分。這是我們Topic實體所採用的策略,
Topic和PollTopic都儲存到同一張表中,透過TOPIC_TYPE欄位進行區分,Topic在T_TOPIC表中對應TOPIC_TYPE= 1的記錄,而PollTopic對應TOPIC_TYPE=2的記錄(稍後在PollTopic實體中指定);
區別的欄位透過 @DiscriminatorColumn 說明,區分欄位對應該實體的值透過@DiscriminatorValue 指定;
JOINED:
父子類相同的部分儲存在同一個表中,不同的部分分開存放,透過表連接獲取完整資料;
TABLE_PER_CLASS:
每一個類對應自己的表,一般不推薦採用這種方式。
Java程式碼
@DiscriminatorColumn(name = "TOPIC_TYPE", discriminatorType =DiscriminatorType.INTEGER, length = 1) ②
@DiscriminatorValue(value="1")③
關聯關係
JPA規範規定任何屬性都預設對映到表中,所以雖然我們沒有給multiple屬性提供註解資訊,但JPA將按照預設的規則對該欄位進行對映:欄位名和屬性名相同,型別相同。
如果我們不希望將某個屬性持久化到資料表中,則可以透過 @Transient 註解顯式指定: @Transient
我們透過@OneToMany 指定了一個一對多的關聯關係,一個PollTopic包括多個 PollOption物件(我們將在稍後的PollOption中透過ManyToOne描述PollOption和PollTopic的關係,以建立 PollTopic和PollOption的雙向關聯關係)。
@OneToMany(mappedBy="pollTopic",cascade=CascadeType.ALL) ④
private Set options = new HashSet();
@OneToMany 中透過mappedBy屬性指定“Many”方類引用“One”方類的屬性名,這裡mappedBy="pollTopic"表示PollOption實體擁有一個指定PollTopic的pollTopic屬性。
@ManyToOne ①
@JoinColumn(name="TOPIC_ID", nullable=false) ②
private PollTopic pollTopic;
在①處透過@ManyToOne描述了PollOption和PollTopic的多對一關聯關係,並透過@JoinColumn指定關聯PollTopic實體所對應表的“外來鍵”,如②所示。
Lob欄位
在JPA中Lob型別型別的持久化很簡單,僅需要透過特殊的Lob註解就可以達到目的。下面,我們對Post中的Lob屬性型別進行標註:
@Lob ①-1
@Basic(fetch = FetchType.EAGER) ①-2
@Column(name = "POST_TEXT", columnDefinition = "LONGTEXT NOT NULL") ①-3
private String postText;
postText屬性對應T_POST表的POST_TEXT欄位,該欄位的型別是LONTTEXT,並且非空。
JPA 透過@Lob將屬性標註為Lob型別,如①-1和②-1所示。
透過@Basic指定Lob型別資料的獲取策略,FetchType.EAGER表示非延遲載入,而FetchType. LAZY表示延遲載入,如①-2和②-2所示。
透過@Column的columnDefinition屬性指定資料表對應的Lob欄位型別,如①-3和② -3所示。
1.JPA概述
JPA(Java Persistence API)作為Java EE 5.0平臺標準的ORM規範,將得到所有Java EE伺服器的支援。
Sun這次吸取了之前EJB規範慘痛失敗的經歷,在充分吸收現有ORM框架的基礎上,得到了一個易於使用、伸縮性強的ORM規範。
從目前的開發社群的反應上看,JPA受到了極大的支援和讚揚,JPA作為ORM領域標準化整合者的目標應該不難實現。
JPA透過JDK 5.0註解或XML描述物件-關係表的對映關係,並將執行期的實體物件持久化到資料庫中。
Sun引入新的JPA ORM規範出於兩個原因:
其一,簡化現有Java EE和Java SE應用的物件持久化的開發工作;
其二,Sun希望整合對ORM技術,實現天下歸一。
JPA由EJB 3.0軟體專家組開發,作為JSR-220實現的一部分。
但它不囿於EJB 3.0,你可以在Web應用、甚至桌面應用中使用。
JPA的宗旨是為POJO提供持久化標準規範,由此可見,經過這幾年的實踐探索,能夠脫離容器獨立執行,方便開發和測試的理念已經深入人心了。
目前Hibernate 3.2、TopLink 10.1.3以及OpenJpa都提供了JPA的實現。
JPA的總體思想和現有Hibernate、TopLink,JDO等ORM框架大體一致。
總的來說,JPA包括以下3方面的技術:
1、ORM對映元資料,JPA支援XML和JDK 5.0註解兩種元資料的形式,元資料描述物件和表之間的對映關係,框架據此將實體物件持久化到資料庫表中;
2、JPA 的API,用來操作實體物件,執行CRUD操作,框架在後臺替我們完成所有的事情,開發者從繁瑣的JDBC和SQL程式碼中解脫出來。
3、查詢語言,這是持久化操作中很重要的一個方面,透過面向物件而非面向資料庫的查詢語言查詢資料,避免程式的SQL語句緊密耦合。
2、實體物件
訪問資料庫前,我們總是要設計在應用層承載資料的領域物件(Domain Object),ORM框架將它們持久化到資料庫表中。
為了方便後面的講解,我們用論壇應用為例,建立領域物件:
Topic -----(extends)----> PollTopic 1--------------*> PollOption
Topic是論壇的主題,而PollTopic是調查性質的論壇主題,擴充套件於Topic,一個調查主題擁有多個選項PollOption。
這三個領域物件很好地展現了領域物件之間繼承和關聯這兩大核心的關係。這3個領域物件將被對映到資料庫的兩張表中:
T_topic程式碼
topic_id id <pk>
topic_title varchar(100)
topic_time datetime
topic_views int
topic_type tinyint
multiple tinyint
max_choice tinyint
T_polloption程式碼
option_id int <pk>
topic_id int
votes int
displayorder tinyint
option varchar(80)
其中,Topic及其子類PollTopic將對映到同一張t_topic表中,並用topic_type欄位區分兩者。而PollOption對映到t_polloption中。
具有ORM元資料的領域物件稱為實體(Entity),按JPA的規範,實體具備以下的條件:
1、必須使用javax.persistence.Entity註解或者在XML對映檔案中有對應的元素;
2、必須具有一個不帶參的建構函式,類不能宣告為final,方法和需要持久化的屬性也不能宣告為final;
3、如果遊離狀的實體物件需要以值的方式進行傳遞,如通Session bean的遠端業務介面傳遞,則必須實現Serializable介面;
4、需要持久化的屬性,其訪問修飾符不能是public,它們必須透過實體類方法進行訪問。
3.使用註解元資料
@Entity:
將領域物件標註為一個實體,表示需要儲存到資料庫中,預設情況下類名即為表名,透過name屬性顯式指定表名,如name = "T_TOPIC",表示Topic儲存到T_TOPIC表中;
@Id :
對應的屬性是表的主鍵;
@GeneratedValue:
主鍵的產生策略,透過strategy屬性指定。預設情況下,JPA自動選擇一個最適合底層資料庫的主鍵生成策略:
如SqlServer對應identity,MySql對應auto increment。
在javax.persistence.GenerationType中定義了以下幾種可供選擇的策略:
1) IDENTITY:表自增鍵欄位,Oracle不支援這種方式;
2) AUTO: JPA自動選擇合適的策略,是預設選項;
3) SEQUENCE:透過序列產生主鍵,透過@SequenceGenerator註解指定序列名,MySql不支援這種方式;
4) TABLE:透過表產生主鍵,框架藉由表模擬序列產生主鍵,使用該策略可以使應用更易於資料庫移植。
不同的JPA實現商生成的表名是不同的:
如 OpenJPA生成openjpa_sequence_table表
Hibernate生成一個hibernate_sequences表,
而TopLink則生成sequence表。這些表都具有一個序列名和對應值兩個欄位,如SEQ_NAME和SEQ_COUNT。
@Column(name = "TOPIC_ID"):
屬性對應的表字段。我們並不需要指定表字段的型別,因為JPA會根據反射從實體屬性中獲取型別;如果是字串型別,我們可以指定欄位長度,以便可以自動生成DDL語句;
@Temporal(TemporalType.DATE):
如果屬性是時間型別,因為資料表對時間型別有更嚴格的劃分,所以必須指定具體時間型別。在javax.persistence.TemporalType列舉中定義了3種時間型別:
1) DATE :等於java.sql.Date
2) TIME :等於java.sql.Time
3) TIMESTAMP :等於java.sql.Timestamp
繼承關係
Topic和PollTopic是父子類,JPA 採用多種方法來支援實體繼承。在父類中必須宣告繼承實體的對映策略。
對於繼承的實體,在javax.persistence.InheritanceType定義了3種對映策略:
SINGLE_TABLE:
父子類都儲存到同一個表中,透過欄位值進行區分。這是我們Topic實體所採用的策略,
Topic和PollTopic都儲存到同一張表中,透過TOPIC_TYPE欄位進行區分,Topic在T_TOPIC表中對應TOPIC_TYPE= 1的記錄,而PollTopic對應TOPIC_TYPE=2的記錄(稍後在PollTopic實體中指定);
區別的欄位透過 @DiscriminatorColumn 說明,區分欄位對應該實體的值透過@DiscriminatorValue 指定;
JOINED:
父子類相同的部分儲存在同一個表中,不同的部分分開存放,透過表連接獲取完整資料;
TABLE_PER_CLASS:
每一個類對應自己的表,一般不推薦採用這種方式。
Java程式碼
@DiscriminatorColumn(name = "TOPIC_TYPE", discriminatorType =DiscriminatorType.INTEGER, length = 1) ②
@DiscriminatorValue(value="1")③
關聯關係
JPA規範規定任何屬性都預設對映到表中,所以雖然我們沒有給multiple屬性提供註解資訊,但JPA將按照預設的規則對該欄位進行對映:欄位名和屬性名相同,型別相同。
如果我們不希望將某個屬性持久化到資料表中,則可以透過 @Transient 註解顯式指定: @Transient
我們透過@OneToMany 指定了一個一對多的關聯關係,一個PollTopic包括多個 PollOption物件(我們將在稍後的PollOption中透過ManyToOne描述PollOption和PollTopic的關係,以建立 PollTopic和PollOption的雙向關聯關係)。
Java程式碼
@OneToMany(mappedBy="pollTopic",cascade=CascadeType.ALL) ④
private Set options = new HashSet();
@OneToMany 中透過mappedBy屬性指定“Many”方類引用“One”方類的屬性名,這裡mappedBy="pollTopic"表示PollOption實體擁有一個指定PollTopic的pollTopic屬性。
Java程式碼
@ManyToOne ①
@JoinColumn(name="TOPIC_ID", nullable=false) ②
private PollTopic pollTopic;
在①處透過@ManyToOne描述了PollOption和PollTopic的多對一關聯關係,並透過@JoinColumn指定關聯PollTopic實體所對應表的“外來鍵”,如②所示。
Lob欄位
在JPA中Lob型別型別的持久化很簡單,僅需要透過特殊的Lob註解就可以達到目的。下面,我們對Post中的Lob屬性型別進行標註:
Java程式碼
@Lob ①-1
@Basic(fetch = FetchType.EAGER) ①-2
@Column(name = "POST_TEXT", columnDefinition = "LONGTEXT NOT NULL") ①-3
private String postText;
postText屬性對應T_POST表的POST_TEXT欄位,該欄位的型別是LONTTEXT,並且非空。
JPA 透過@Lob將屬性標註為Lob型別,如①-1和②-1所示。
透過@Basic指定Lob型別資料的獲取策略,FetchType.EAGER表示非延遲載入,而FetchType. LAZY表示延遲載入,如①-2和②-2所示。
透過@Column的columnDefinition屬性指定資料表對應的Lob欄位型別,如①-3和② -3所示。