JPA詳解

 

2006 年 5 月 2 日,EJB 3.0 規範最終版由 JCP(Java Community Process) 正式公佈,標準號爲 JSR(Java Specification Request)220。EJB 3.0 規範的發佈爲企業應用開發者提供了一種全新的、簡化的 API。制定這組 API 的目標是讓開發變得更加容易,相對於以前版本的 EJB 規範,這組 API 也更加簡單。Java Persistence API 是 EJB 3.0 中負責處理持久化數據管理的部分,目標是爲開發者處理持久化數據庫管理提供標準支持,也成爲 Java EE 容器提供商必須遵守的標準。

EJB 3.0 規範由三部分組成:EJB3.0 Simplified API、EJB 核心規範(EJB Core Contracts and Requirements)和 JPA(Java Persistence API)。

Simplified API

Simplified API 部分主要規定了基於 EJB 3.0 標準開發企業應用時所需要遵守的 Bean 類和接口要求、這些 API 的使用方式以及容器支持等多方面的內容。還詳細的規定了 EJB3.0 中除 Java Persistence API 部分之外的 EJB 實現所支持的註釋(Annotation)。規範中還有專門章節講解 EJB 3.0 和此前的 EJB 規範如何同時工作,以及如何將此前已經開發好的企業應用移植到 EJB 3.0 容器中。其中的 Persistence 的內容放在了 JPA 規範中。

EJB 核心規範

EJB 核心規範中首先描述了 EJB 在企業應用中的角色、EJB 規範的體系結構,確定了支持 EJB 標準的容器應該遵守的準則和要求。隨後從多個角度詳細的介紹了 EJB 體系中各部分的功能需求和實現要求,包括 Session Bean、消息驅動 Bean(Message-Driven Bean)、事務、安全管理、部署描述符等。其中的 Persistence 的內容放在了 JPA 規範中。由於 EJB 3.0 規範並不排斥之前的 EJB 規範,因此 EJB 2.X 和 EJB 1.X 中的內容也保留在了 EJB 核心規範中。

Java Persistence API(JPA)

EJB 2.X 和 EJB 1.X 規範中的實體 Bean(EntityBean)部分都難以使用,使持久化成爲 EJB 規範的一個軟肋,影響了 EJB 標準發揮更大的作用,自然而然的,JPA 成爲了 EJB3.0 規範中被關注最多的部分。JPA 規範部分詳細的介紹了 JPA 中實體 Bean 新的定義,並介紹了實體 Bean 支持的註釋、全新的查詢語言、實體管理接口、容器實現規範等內容。

JPA 標準中引入了新的實體概念,每一個實體都是一個普通的 Java 類,不需要繼承任何其他的接口或者擴展某個指定類,這個 Java 類必須使用 javax.persistence.Entity 進行註釋。JPA 標準中還提供了包括 javax.persistence.Tablejavax.persistence.Id 等在內的多個註釋,用於完成實體和數據庫之前的映射。JPA 中引入了新的查詢語言 JPQL(Java Persistence Query Language),JPQL 允許開發者採用面向對象的查詢語言來查找實體,這些實體持久化在關係型的數據庫中,”select a from Animal a where a.name=’a’”是一個 JPQL 的例子。其中的 Animal 是一個 Java 類,而不是關係型數據庫中的一個表或者視圖。除了簡單的查詢功能之外,JPQL 中還能夠支持 Group、Order 等通常只有 SQL 才能提供的高級功能。JPA 標準中還規定了在 Java EE 環境中和非 Java EE 環境中使用 JPA 時的差異,以及 Java EE 環境中容器的職責等。


 
 


JPA 體系架構

JPA 中定義一套類和接口用於實現持久化管理和對象/關係的映射,下面這張圖中顯示了 JPA 的主要組件以及它們之間的相互關係。


圖1 JPA 主要組件和相互關係

  • EntityManagerFactory

    EntityManagerFactoryEntityManager 的工廠類,負責創建 EntityManager 對象。

  • EntityManager

    EntityManager 是 JPA 應用中使用的基本對象,通過它提供的相應方法可以管理持久化對象,也可以新建或者刪除持久化對象。EntityManager 還負責創建 Query 實例。在容器外使用時,EntityManagerFactoryEntityManager 之間是一對一的關係。

  • Entity

    EntityTransaction 提供 Entity 操作時需要的事務管理,和 EntityManager 是一對一的關係。在查詢操作時不需要使用 EntityTransaction,而在對象持久化、狀態更新、對象刪除等情況下則必須使用顯式的使用 EntityTransaction 的相關方法管理事務。

  • Query

    Query 是查詢實體的接口,Query 對象可以從 EntityManager 中獲得。根據 EJB 3.0 規範中的描述,Query 接口需要同時支持 JPQL 和原生態 SQL 兩種語法。

  • Persistence

    Persistence 是一個工具類,負責根據配置文件提供的參數創建 EntityManagerFactory 對象。

下面的代碼演示瞭如何通過 JPA 提供的接口和 JPQL 查詢語言完成實體查詢和更新的例子,例子中的代碼假定運行在非 Java EE 環境中。


清單 1 在非 Java EE 環境使用 JPA 接口的例子

/*2. * Persistence 類獲取 EntityManagerFactory 實例; 3. * 一般 EntityManagerFactory 實例被緩存起來重複使用,4. * 避免重複創建 EntityManagerFactory 實例引起的性能影響5. */6. EntityManagerFactory factory = 7. Persistence.createEntityManagerFactory (“mysql”);8.9. // 從 EntityManagerFactory 實例 factory 中獲取 EntityManager10. EntityManager em = factory.11. createEntityManager(PersistenceContextType.EXTENDED);12.13. // 實體的更新需要在事務中運行14. EntityTransaction tx = em.getTransaction ();15. tx.begin ();16.17. // 查找所有公司中的女性僱員18. Query query = em.createQuery ("select e from Employee e " 19. + " where e.sex = 'femail'");20. List results = query.getResultList ();21.22. // 給所有女性僱員增加半天假期23. for (Object res : results){ 24. Employee emp = (Employee) res; 25. emp.setHoliday (emp.getHoliday () +0.5);}26.27. // 提交事務(持久化所有更新)28. tx.commit ();29. em.close ();30. factory.close ();

下面的代碼顯示了在 EJB 容器中開發 JPA 應用時的接口使用情況,由於容器中的 EntityManager 是注入的,事務也是聲明式的,因此在容器中完成上面的業務邏輯要簡單得多。


清單 2 在容器中運行的 JPA 例子

                
1.	  /*
2.	   * 在容器中運行 JPA 應用時,EntityManager 接口的實例”em”
3.	   * 是通過 @Resource 註釋注入的。事務也通常是聲明式的。
4.	   */
5.	  // 查找所有公司中的女性僱員
6.	  Query query = em.createQuery ("select e from Employee e "   
7.	      + " where e.sex = 'femail'");
8.	  List results = query.getResultList ();
9.	
10.	  // 給所有女性僱員增加半天假期
11.	  for (Object res : results){    
12.	      Employee emp = (Employee) res;    
13.	      emp.setHoliday (emp.getHoliday () +0.5);}

                
1.	/*
2.	* Persistence 類獲取 EntityManagerFactory 實例; 
3.	* 一般 EntityManagerFactory 實例被緩存起來重複使用,
4.	* 避免重複創建 EntityManagerFactory 實例引起的性能影響
5.	*/
6.	  EntityManagerFactory factory = 
7.	      Persistence.createEntityManagerFactory (“mysql”);
8.	
9.	  // 從 EntityManagerFactory 實例 factory 中獲取 EntityManager
10.	  EntityManager em = factory.
11.	      createEntityManager(PersistenceContextType.EXTENDED);
12.	
13.	  // 實體的更新需要在事務中運行
14.	  EntityTransaction tx = em.getTransaction ();
15.	  tx.begin ();
16.	
17.	  // 查找所有公司中的女性僱員
18.	  Query query = em.createQuery ("select e from Employee e "   
19.	      + " where e.sex = 'femail'");
20.	  List results = query.getResultList ();
21.	
22.	  // 給所有女性僱員增加半天假期
23.	  for (Object res : results){    
24.	      Employee emp = (Employee) res;    
25.	      emp.setHoliday (emp.getHoliday () +0.5);}
26.	
27.	  // 提交事務(持久化所有更新)
28.	  tx.commit ();
29.	  em.close ();
30.	  factory.close ();

下面的代碼顯示了在 EJB 容器中開發 JPA 應用時的接口使用情況,由於容器中的 EntityManager 是注入的,事務也是聲明式的,因此在容器中完成上面的業務邏輯要簡單得多。


清單 2 在容器中運行的 JPA 例子
                
1.	  /*
2.	   * 在容器中運行 JPA 應用時,EntityManager 接口的實例”em”
3.	   * 是通過 @Resource 註釋注入的。事務也通常是聲明式的。
4.	   */
5.	  // 查找所有公司中的女性僱員
6.	  Query query = em.createQuery ("select e from Employee e "   
7.	      + " where e.sex = 'femail'");
8.	  List results = query.getResultList ();
9.	
10.	  // 給所有女性僱員增加半天假期
11.	  for (Object res : results){    
12.	      Employee emp = (Employee) res;    
13.	      emp.setHoliday (emp.getHoliday () +0.5);}

JPA全稱爲Java Persistence API ,Java持久化API是Sun公司在Java EE 5規範中提出的Java持久化接口。JPA吸取了目前Java持久化技術的優點,旨在規範、簡化Java對象的持久化工作。使用JPA持久化對象,並不是依賴於某一個ORM框架。

爲什麼要使用JAP?

在說爲什麼要使用JPA之前,我們有必要了解爲什麼要使用ORM技術。

ORM 是Object-Relation-Mapping,即對象關係影射技術,是對象持久化的核心。ORM是對JDBC的封裝,從而解決了JDBC的各種存在問題:

a) 繁瑣的代碼問題

用JDBC的API編程訪問數據庫,代碼量較大,特別是訪問字段較多的表的時候,代碼顯得繁瑣、累贅,容易出錯。例如:PreparedStatement pstmt=con.prepareStatment("insert into account value(?,?,?,?,?,?,?,?,?)");

ORM則建立了Java對象與數據庫對象之間的影射關係,程序員不需要編寫複雜的SQL語句,直接操作Java對象即可,從而大大降低了代碼量,也使程序員更加專注於業務邏輯的實現。

b) 數據庫對象連接問題

關係數據對象之間,存在各種關係,包括1對1、1對多、多對1、多對多、級聯等。在數據庫對象更新的時候,採用JDBC編程,必須十分小心處理這些關係,以保證維持這些關係不會出現錯誤,而這個過程是一個很費時費力的過程。

ORM建立Java對象與數據庫對象關係影射的同時,也自動根據數據庫對象之間的關係創建Java對象的關係,並且提供了維持這些關係完整、有效的機制。

c) 系統架構問題

JDBC屬於數據訪問層,但是使用JDBC編程時,必須知道後臺是用什麼數據庫、有哪些表、各個表有有哪些字段、各個字段的類型是什麼、表與表之間什麼關係、創建了什麼索引等等與後臺數據庫相關的詳細信息。

使用ORM技術,可以將數據庫層完全隱蔽,呈獻給程序員的只有Java的對象,程序員只需要根據業務邏輯的需要調用Java對象的Getter和 Setter方法,即可實現對後臺數據庫的操作,程序員不必知道後臺採用什麼數據庫、有哪些表、有什麼字段、表與表之間有什麼關係。

d) 性能問題

採用JDBC編程,在很多時候存在效率低下的問題。

pstmt =conn.prepareStatement("insert into user_info values(?,?)");

for (int i=0; i<1000; i++) {

    pstmt.setInt(1,i);

    pstmt.setString(2,"User"+i.toString());

    pstmt.executeUpdate();

}

以上程序將向後臺數據庫發送1000次SQL語句執行請求,運行效率較低。

採用ORM技術,ORM框架將根據具體數據庫操作需要,會自動延遲向後臺數據庫發送SQL請求,ORM也可以根據實際情況,將數據庫訪問操作合成,儘量減少不必要的數據庫操作請求。

JPA是目前比較流行的一種ORM技術之一,所以他擁有ORM技術的各種特點,當然他還有自己的一些優勢:

1 標準化

JPA 是 JCP 組織發佈的 Java EE 標準之一,因此任何聲稱符合 JPA 標準的框架都遵循同樣的架構,提供相同的訪問 API,這保證了基於JPA開發的企業應用能夠經過少量的修改就能夠在不同的JPA框架下運行。

2 對容器級特性的支持

JPA 框架中支持大數據集、事務、併發等容器級事務,這使得 JPA 超越了簡單持久化框架的侷限,在企業應用發揮更大的作用。

3 簡單易用,集成方便

JPA的主要目標之一就是提供更加簡單的編程模型:在JPA框架下創建實體和創建Java 類一樣簡單,沒有任何的約束和限制,只需要使用 javax.persistence.Entity進行註釋;JPA的框架和接口也都非常簡單,沒有太多特別的規則和設計模式的要求,開發者可以很容易的掌握。JPA基於非侵入式原則設計,因此可以很容易的和其它框架或者容器集成。

4 可媲美JDBC的查詢能力

JPA的查詢語言是面向對象而非面向數據庫的,它以面向對象的自然語法構造查詢語句,可以看成是Hibernate HQL的等價物。JPA定義了獨特的JPQL(Java Persistence Query Language),JPQL是EJB QL的一種擴展,它是針對實體的一種查詢語言,操作對象是實體,而不是關係數據庫的表,而且能夠支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能夠提供的高級查詢特性,甚至還能夠支持子查詢。

5 支持面向對象的高級特性

JPA 中能夠支持面向對象的高級特性,如類之間的繼承、多態和類之間的複雜關係,這樣的支持能夠讓開發者最大限度的使用面向對象的模型設計企業應用,而不需要自行處理這些特性在關係數據庫的持久化。

怎麼使用JPA?

簡單說來要使用JPA需要網工程裏面導入jar包(不同的提供商提供的jar包也不同),並且還要有一個persistence.xml文件來設置持久性單元。

如果你打算在你的應用中使用JPA,你就需要使用持久性單元(Persistence Units)。持久性單元具有唯一的名稱,負責定義應用中的一組實體如何進行管理和持久性。在應用中使用persistence.xml文件來設置持久性單元,可以配置多個持久性單元,但每個單元擁有唯一的名稱。

注意:persistence.xml文件的位置決定持久性的根(Persistence Root)。持久性的根爲JAR文件或者包含META-INF目錄(前提是persistence.xml位於此)的目錄。一般將這個persistence.xml文件放在src下的META-INF中。持久性單元包含的屬性有:

在該持久性單元範圍(作用域)內的實體類
爲上述實體類提供持久性的持久性提供者(Persistence Provider)或庫
上述實體類的數據源(Data Source)
應用使用的事務類型(Transaction Type)
持久性提供者(Persistence Provider)

持久性提供者指的是JPA的實現。持久性提供者是一個能夠爲應用提供持久性對象的的庫。例如Netbeans綁定了TopLink Essentials 作爲持久性提供者,同時TopLink Essentials也是Sun Java System Application Server Platform Edition 9 (Glassfish)的參考實現和默認配置。TopLink Essentials包括toplink-essentials.jar和toplink-essentials-agent.jar兩個JAR文件。你可以使用別的持久性提供者例如Hibernate。

數據源

數據源指的是提供實體存儲的數據庫。數據源必須在服務器中註冊並且使用JNDI名稱指定。如果事務是由容器管理的JTA事務那麼數據源必須是JTA數據源。如果事務是應用負責管理的,數據源根據在IDE中註冊的JDBC數據庫連接指定。在Java SE環境下,數據庫可以通過數據源指定,也可以使用其他方法,取決於持久性提供者的要求。

事務類型

持久性單元指定事務是如何管理的。事務類型取決於目標容器,如果目標容器是Java EE容器,你可以使用容器管理或者應用管理。如果不是這樣的話,你只能使用應用管理。

容器管理的事務(JTA事務)
容器使用Java Transaction API來管理事務。你必須將你的應用部署在Java EE容器中,並且你的數據源必須支持JTA。在persistence.xml中事務類型被設置爲JTA.如果你使用Glassfish這個是默認選項。

應用管理的事務(本地資源事務:Resource-Local Transaction)

由應用負責事務處理。在persistence.xml文件中,被設置爲RESOUCE_LOCAL

一個簡單的persistence.xml配置文件例子:

<?xml version="1.0" encoding="UTF-8"?>

<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">

<persistence-unit name="lite_db_pu"                                                                  transaction-type="RESOURCE_LOCAL">

<provider>oracle.toplink.essentials.PersistenceProvider</provider>

<class>com.e.liteworkflow.entity.Workflow</class>

<class>com.e.liteworkflow.entity.Activity</class>

<properties>

   <property name="toplink.jdbc.user" value="sa"/>

   <property name="toplink.jdbc.password" value="E2005"/>

   <property name="toplink.jdbc.url"

       value="jdbc:jtds:sqlserver://10.30.20.69/liteworkflowdb"/>

       <property name="toplink.jdbc.driver"                                               value="net.sourceforge.jtds.jdbc.Driver"/>

   <!--property name="toplink.logging.level" value="FINE"/-->

   <property name="toplink.ddl-generation" value="create-tables"/>

   <property name="toplink.weaving" value="false"/>

</properties>

</persistence-unit>

<persistence-unit name="lite_derby_pu"                                                           transaction-type="RESOURCE_LOCAL">

       <!— 省略了配置內容 -->

</persistence-unit>

</persistence>

對配置文件中一些重要元素和屬性的解釋:

配置文件中可以有多個<persistence-unit></persistence-unit>但是這些persistence-unit的name屬性不可以相同;
persistence-unit的transaction-type標明瞭事務由誰去管理,RESOURCE_LOCAL指本地數據庫管理事務(不支持分佈式事務),而JTA則是由託管的容器來管理事務(支持分佈式事務);
<provider></provider>聲明瞭類文件,這個類文件提供初始的factory方法來創建一個EntityManager實例;
<class></class>列出應用程序中的實體類的名稱(這裏要有完整的包名和類名);
<properties></properties>中的<property/>均是設置連接數據庫的相關信息:

屬性名:toplink.weaving

描述:是否加載實體使用織入,如果對@OneToOne和@ManyToOne的關係實體使用惰性加載,則必須使用織入的方式。

可選值:true(默認):織入實體。

        false:不織入實體。

static:靜態織入實體,如果運行在不是Java EE 5容器中,或者JDK 不支持代理

            -javaagent:toplink-essentials-agent.jar方式運行時,需要設置此選項。


屬性名:toplink.ddl-generation

描述:指定DDL生成方式,這些值定義在cle.toplink.essentials.ejb.cmp3.EntityManager FactoryProvider類中。

可選值:none(默認):不生成DDL,不自動生成數據庫。

create-tables:只生成不存在的表,不改變原有表結構。

(create-tables屬性值。如果表存在的話,代碼執行結果就向存在的表格中插入記錄,並且生成很多的警告信

息,告訴你存在了那些表)

drop-and-create-tables:刪除原表,重新生成新表。

數據源的指定:根據自己的實現和應用服務器的默認行爲,可能需要爲jta-data-source(指定JTA數據源)和/或non-jta-data-source(指定非JTA數據源)設置提供值。

實體對象以及註解介紹

具有ORM元數據的領域對象稱爲實體(Entity),按JPA的規範,實體具備以下的條件:

必須使用javax.persistence.Entity註解或者在XML映射文件中有對應的元素;

必須具有一個不帶參的構造函數,類不能聲明爲final,方法和需要持久化的屬性也不能聲明爲final;

如果遊離狀的實體對象需要以值的方式進行傳遞,如通Session bean的遠程業務接口傳遞,則必須實現Serializable接口;

需要持久化的屬性,其訪問修飾符不能是public,它們必須通過實體類方法進行訪問。

使用註解元數據

基本註解:例子:

@Entity(name = "T_TOPIC") ①

public class Topic implements Serializable ...{

@Id ②-1

@GeneratedValue(strategy = GenerationType.TABLE) ②-2

@Column(name = "TOPIC_ID") ②-3

private int topicId;

@Column(name = "TOPIC_TITLE", length = 100) ③

private String topicTitle;

@Column(name = "TOPIC_TIME") @Temporal(TemporalType.DATE) ④

private Date topicTime;

@Column(name = "TOPIC_VIEWS")

private int topicViews;

...  

}

解釋:


① Entity標明該類(Topic)爲一個實體類,它對應數據庫中的表表名是T_TOPIC,這裏也可以寫成:   @Entity

    @Table(name = "T_TOPIC") 其作用都是一樣的


②-1 Id標明該屬性對應數據表中的主鍵

②-2 GeneratedValue通過strategy屬性指明主鍵生成策略,默認情況下,JPA自動選擇一個最適合底層數據庫的主鍵生成策略。在javax.persistence.GenerationType中定義了以下幾種可供選擇的策略:


1) IDENTITY:表自增鍵字段,Oracle不支持這種方式;

2) AUTO: JPA自動選擇合適的策略,是默認選項;

3) SEQUENCE:通過序列產生主鍵,通過@SequenceGenerator註解指定序列名,MySql不支持這種方式;

4) TABLE:通過表產生主鍵,框架藉由表模擬序列產生主鍵,使用該策略可以使應用更易於數據庫移植。

②-3 Column標明這個屬性是數據表中的一列,該列的名字是TOPIC_ID


③ Column的一個屬性length指明的是該屬性的允許的長度。(個人認爲設定該屬性只是對於程序中操作該屬性時增加了一驗證過程,對數據庫中該列原來的設置並沒有影響,但是length屬性指定的值必須不能大於數據庫創建表時給該列限制的最大長度否則會出錯)

④ Temporal(TemporalType.DATE):如果屬性是時間類型,因爲數據表對時間類型有更嚴格的劃分,所以必須指定具體時間類型。在javax.persistence.TemporalType枚舉中定義了3種時間類型:


1) DATE :等於java.sql.Date

2) TIME :等於java.sql.Time

3) TIMESTAMP :等於java.sql.Timestamp

繼承關係註解:

對繼承關係進行註解,必須在父類中聲明繼承實體的映射策略。

例子


@Entity(name = "T_TOPIC")

@Inheritance(strategy = InheritanceType.SINGLE_TABLE) ① @DiscriminatorColumn(name = "TOPIC_TYPE", discriminatorType =

DiscriminatorType.INTEGER, length = 1) ②

@DiscriminatorValue(value="1")③

public class Topic implements Serializable ...{ … }


解釋:

① Inheritance通過strategy屬性指明實體的繼承策略。

在javax.persistence.InheritanceType定義了3種映射策略:

1) SINGLE_TABLE:父子類都保存到同一個表中,通過字段值進行區分。

2) JOINED:父子類相同的部分保存在同一個表中,不同的部分分開存放,通過表連接獲取完整數據;

3) TABLE_PER_CLASS:每一個類對應自己的表,一般不推薦採用這種方式。


② DiscriminatorColumn如果繼承策略採用第一種繼承策略,則需要指明區分父子類的字段,DiscriminatorColumn就是用來指明區分字段的註解。

③DiscriminatorValue 同樣的採用第一種繼承策略通過字段區分父子類,則用這個註解給該實體的區分字段賦值在這裏賦的值爲”1”.

關聯關係註解:

例子:

@Entity @DiscriminatorValue(value="2") ①

public class PollTopic extends Topic ...{②繼承於Topic實體

private boolean multiple; ③

@Column(name = "MAX_CHOICES")

private int maxChoices; @OneToMany(mappedBy="pollTopic",cascade=CascadeType.ALL) ④

private Set options = new HashSet();

//省略get/setter方法

}

解釋:


① 通過@DiscriminatorValue將區分字段TOPIC_TYPE的值爲2。由於PollTopic實體繼承於Topic實體,其它的元數據信息直接從Topic獲得。

④ OneToMany指定了一個一對多的關聯關係,mappedBy屬性指定“Many”方類引用“One”方類的屬性名;cascade屬性指明瞭級聯方式(如果這裏不指定爲CascadeType.ALL的話,那麼有關聯關係的兩個對象在做保存和刪除操作時要分別來進行)建議:儘可能使用cascade=CascadeType.ALL來減少持久化操作的複雜性和代碼量

注意:JPA規範規定任何屬性都默認映射到表中,所以雖然我們沒有給③處的multiple屬性提供註解信息,但JPA將按照 默認的規則對該字段進行映射:字段名和屬性名相同,類型相同。如果我們不希望將某個屬性持久化到數據表中,則可以通過@Transient註解顯式指定:

@Transient

private boolean tempProp1;

@Entity(name="T_POLL_OPTION")

Public class PollOption implements Serializable ...{

@Id

@GeneratedValue(strategy = GenerationType.TABLE)

@Column(name = "OPTION_ID")

private int optionId;

@Column(name = "OPTION_ITEM")

private String optionItem;

@ManyToOne ①

@JoinColumn(name="TOPIC_ID", nullable=false) ②

private PollTopic pollTopic;

}


解釋:

① ManyToOne描述了多對一的關聯關係,他是對該類引用的”One”類(PollTopic)的屬性(pollTopic)進行註解的。

② JoinColumn指定關聯”One”(PollTopic)實體所對應表的“外鍵”。


Lob字段的註解:
在JPA中Lob類型類型的持久化很簡單,僅需要通過特殊的Lob註解就可以達到目的。

例子:

@Lob ①-1

@Basic(fetch = FetchType.EAGER) ①-2

@Column(name = "POST_TEXT", columnDefinition = "LONGTEXT NOT NULL") ①-3

private String postText;

@Lob

@Basic(fetch = FetchType. LAZY) ②-2

@Column(name = "POST_ATTACH", columnDefinition = "BLOB") ②-3

private byte[] postAttach;


解釋:

①-1 JPA 通過@Lob將屬性標註爲Lob類型 ;

①-2 通過@Basic指定Lob類型數據的獲取策略,FetchType.EAGER表示非延遲 加載,而FetchType. LAZY表示延遲加載 ;

①-3 通過@Column的columnDefinition屬性指定數據表對應的Lob字段類型。


使用XML元數據

除了使用註解提供元數據信息外,JPA也允許我們通過XML提供元數據信息。按照JPA的規範,如果你提供了XML元數據描述信息,它將覆蓋實體類中的註解元數據信息。XML元數據信息以 orm.xml命名,放置在類路徑的META-INF目錄下。

<?xml version="1.0" encoding="UTF-8"?>

<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"

version="1.0">

①實體對象所在的包

<package>com.baobaotao.domain</package>

<entity class="Topic">

②Topic實體配置

<table name="T_TOPIC" />

<attributes>

<id name="topicId">

<column name="TOPIC_ID"/>

<generated-value strategy="TABLE" />

</id>

<basic name="topicTitle">

<column name="TOPIC_TITLE" length="30" />

</basic>

<basic name="topicTime">

<column name="TOPIC_TIME" />

<temporal>DATE</temporal>

</basic>

<basic name="topicViews">

<column name="TOPIC_VIEWS" />

</basic>

</attributes>

</entity>

<entity class="PollTopic">

②PollTopic實體配置

<discriminator-value>2</discriminator-value>

<attributes>

<basic name="maxChoices">

<column name="MAX_CHOICES" />

</basic>

<one-to-many name="options" mapped-by="pollTopic">

<cascade>

<cascade-all/>

</cascade>

</one-to-many>

</attributes>

</entity>

<entity class="PollOption">

②PollOption實體配置

<table name="T_POLL_OPTION" />

<attributes>

<id name="optionId">

<column name="OPTION_ID" />

<generated-value strategy="TABLE" />

</id>

<basic name="optionItem">

<column name="OPTION_ITEM"/>

</basic>

<many-to-one name="pollTopic" >

<join-column name="TOPIC_ID" nullable="false"/>

</many-to-one>

</attributes>

</entity>

<entity class="Post">

②Post實體配置

<table name="T_POST" />

<attributes>

<id name="postId">

<column name="POST_ID" />

<generated-value strategy="TABLE" />

</id>

<basic name="postText" fetch="EAGER">

<column name="POST_TEXT" column-definition="LONGTEXT NOT NULL"/>

<lob/>

</basic>

<basic name="postAttach" fetch="LAZY">

<column name="POST_ATTACH" column-definition="BLOB"/>

<lob/>

</basic>

</attributes>

</entity>

</entity-mappings>

使用這個orm.xml來描述實體信息的話,這裏並沒有標明兩個繼承類之間的關係,其繼承信息將從實體類反射信息獲取。

到這裏我們的實體描述結束了,當然我們只是做了比較簡單的描述,對於那些複雜的信息描述並沒有進行講述。實體描述結束了,有人會問如果我要來操作這些實體該怎麼操作?這就是我們接下來要講述的問題。

EntityManager介紹

實體對象由實體管理器進行管理,JPA使用javax.persistence.EntityManager代表實體管理器。實體管理器和持久化上下文關聯,持久化上下文是一系列實體的管理環境,我們通過EntityManager和持久化上下文進行交互。

有兩種類型的實體管理器:

容器型:容器型的實體管理器由容器負責實體管理器之間的協作,在一個JTA事務中,一個實體管理器的持久化上下文的狀態會自動廣播到所有使用EntityManager的應用程序組件中。Java EE應用服務器提供的就是管理型的實體管理器;

應用程序型:實體管理器的生命週期由應用程序控制,應用程序通過javax.persistence.EntityManagerFactory的createEntityManager創建EntityManager實例。

EntityManager的創建過程圖:

我們在程序中的創建EntityManager的代碼:

EntityManagerFactory currentManagerFactory = Persistence.createEntityManagerFactory(persistenceUtilName);

EntityManager em = currentManagerFactory.createEntityManager();

實體的狀態

實體對象擁有以下4個狀態,這些狀態通過調用EntityManager接口方法發生遷移:

1) 新建態:新創建的實體對象,尚未擁有持久化主鍵,沒有和一個持久化上下文關聯起來。

2) 受控態:已經擁有持久化主鍵並和持久化上下文建立了聯繫;

3) 遊離態:擁有持久化主鍵,但尚未和持久化上下文建立聯繫;

4) 刪除態:擁有持久化主鍵,已經和持久化上下文建立聯繫,但已經被安排從數據庫中刪除。

通過EntityManager中的接口方法可以改變實體對象的狀態:

a) void persist(Object entity)

通過調用EntityManager的persist()方法,新實體實例將轉換爲受控狀態。這意謂着當persist ()方法所在的事務提交時,實體的數據將保存到數據庫中。如果實體已經被持久化,那麼調用persist()操作不會發生任何事情。如果對一個已經刪除的 實體調用persist()操作,刪除態的實體又轉變爲受控態。如果對遊離狀的實體執行persist()操作,將拋出 IllegalArgumentException。

在一個實體上調用persist()操作,將廣播到和實體關聯的實體上,執行相應的級聯持久化操作;

b) void remove(Object entity)

通過調用remove()方法刪除一個受控的實體。如果實體聲明爲級聯刪除(cascade=REMOVE 或者cascade=ALL ),被關聯的實體也會被刪除。在一個新建狀態的實體上調用remove()操作,將被忽略。如果在遊離實體上調用remove()操作,將拋出 IllegalArgumentException,相關的事務將回滾。如果在已經刪除的實體上執行remove()操作,也會被忽略;

c) void flush()

將受控態的實體數據同步到數據庫中;

d) T merge(T entity)

將一個遊離態的實體持久化到數據庫中,並轉換爲受控態的實體;

e) T find(Class entityClass, Object primaryKey)

以主鍵查詢實體對象,entityClass是實體的類,primaryKey是主鍵值;

f) Query

JPA使用javax.persistence.Query接口代表一個查詢實例,Query實例由EntityManager通過指定查詢語句構建。該接口擁有衆多執行數據查詢的接口方法:◆Object getSingleResult():執行SELECT查詢語句,並返回一個結果;

◆List getResultList() :執行SELECT查詢語句,並返回多個結果;

◆Query setParameter(int position, Object value):通過參數位置號綁定查詢語句中的參數,如果查詢語句使用了命令參數,則可以使用Query setParameter(String name, Object value)方法綁定命名參數;

◆Query setMaxResults(int maxResult):設置返回的最大結果數;

◆int executeUpdate():如果查詢語句是新增、刪除或更改的語句,通過該方法執行更新操作;

還有就是關於JPA的查詢語言,JPA的查詢語言是面向對象而非面向數據庫的,它以面向對象的自然語法構造查詢語句,其查詢語言類似於HQL語句,在這裏就不再贅述。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章