2011年3月29日 星期二

JPA Overview

[Definition]

* Java Persistence API (Java 持久層/永續層 API)

* 通過 註解(Annotation, JDK5.0) 或 XML 描述方式,將 物件-關聯表的映射關係 (O-R Mapping / ORM)和運行期 Entity object 持久化到資料庫中去。

* Object/Relational Mapping (ORM)
  • 簡單的說就是將 Java 中的物件與物件關係,映射至關聯式資料庫中的表格與表格之間的關係
  • 使用JPA,底層可以使用不同廠商的ORM實作,而介面則是JPA的標準,ex:
    • NetBeans + Glassfish,則預設的底層實作為TopLink。
    • JBoss的工具其底層實作則為Hibernate

* Persistence in this context covers three areas:
  • the API itself, defined in the javax.persistence package
  • the Java Persistence Query Language (JPQL)
  • object/relational metadata

* 持久性/永續層
  • 相對於只能存活於 JRE Heap 中的 object 來說, 需要能將大量的 object 資訊與 state 保存在一個儲存體中, 提供經常性重複存取物件屬性的 data 機制, JPA 即提供了一個 Java 標準的 Persistence 解決方案.
  • Persistence 的儲存標的物可以是 file 或是 DB.


[Annotation]

* @Entity: 成為 JPA 的 Entity 類別,或者 XML 映射中有對應的 元素。

* @Table: 標示這個Entity類別對應的資料表格,若類別名稱與表格名稱相同,則可以省略,預設會將類別名稱對應至同名的表格,Entity類別必須實作Serializable介面。

* @Id: 使用此標註在資料成員或Getter方法上,使每個Entity類別必須有獨一無二的識別屬性,並與資料表格的主鍵相對應。

* @GeneratedValue: 可以選擇主鍵的產生策略,ex: @GeneratedValue(strategy = GenerationType.AUTO),在這邊利用資料庫本身的自動產生策略,由底層的資料庫來提供。

* @Column: 若成員名稱與表格欄位名稱一樣,則會自動對應,若不同,則可以使用@Column來指定欄位名稱。


[Implement]

  • 為了JPA必須設定資料庫連結與底層實作的一些細節,您要在META-INF下撰寫一個 persistence.xml
  • 建立EntityManagerFactory,EntityManagerFactory內含設定資訊,負責管理 EntityManager,而這樣的方式所取得的EntityManager,稱之為 Application-Managed EntityManager
  • 取得EntityManager後,可透過getTransaction()取得 EntityTransaction
    • EntityTransaction 負責管理交易,透過EntityManger的persist()方法來儲存User物件,EntityManager會自動將對應的成員儲存至對應的資料表格欄位,
    • 若透過EntityManager的find()方法,指定主鍵來查找資料並封裝為User物件,基本上所有的EntityManager操作,要在交易中完成,但find()可以不用在交易中完成,只不過若不在交易中使用find()方法,則查找回來的Entity將立刻不在EntityManager的管理之中(也就是處於Detached狀態

* 若交易過程中發生錯誤,可以捕捉例外,執行EntityTransaction的rollback()方法來撤回交易。
EntityManager entityManager = 
JPAUtil.getEntityManagerFactory().createEntityManager();
EntityTransaction etx = entityManager.getTransaction();
etx.begin();
entityManager.persist(user);
etx.commit();
entityManager.close();

* 若以Java EE容器來管理JPA相關資源,可以使用資源注入的方式取得EntityManager,並可由容器來為您管理Persistence Context,每個EntityManager會關聯至一個Persistence Context,容器會管理Persistence Context的存活範圍,ex: Transaction-scoped或Extended-scoped,Persistence Context管理一組Entity。

* Container-Managed EntityManager
  • 使用 @PersistenceContext 注入EntityManager實例,所取得的EntityManager。
  • unitName屬性指定了persistence.xml中的Persistence Context名稱,Persistence Context預設為Transaction-scoped,也就是在方法開始前會啟始交易,結束後停止交易,Persistence Context的存活範圍在交易之間。
* EntityManager不是Thread-safe,所以要注意在多執行緒下共用存取的同步問題,ex: 使用Stateful Session Bean,讓每個客戶端取得一個Session Bean實例,並獨自使用一EntityManager。


[persistence.xml]

* transaction-type屬性為JTA,並透過設置容器端管理的Data Source之JNDI名稱,這必須在容器端先定好,JDBC資源與Connection Pool等。

* 若要直接在Servlet中使用JPA:

  • 要記得的是,EntityManager不是Thread-safe,所以切記,不可直接將EntityManager注入Servlet中成為物件成員之一,Servlet被共用存取的情況下,會有資料共用存取的問題。
  • 有兩種方式:
    • Container-Managed Entity: 透過在類別上宣告@PersistenceContext並設定JNDI名稱來查找。
    • Application-Managed EntityManager: 透過 @PersistenceUnit 注入EntityManagerFactory並用其取得EntityManager。

* @PersistenceContext的name名稱將作為 JNDI 查找名稱,從unitName 設定得知,實際上查找到的是中對應的定義,Servlet中使用JNDI查找name名稱以取得 EntityManager實例,並透過注入的UserTransaction啟用交易,在交易中進行持久化動作。

* 在Java EE模組中,Persistence Unit會有個獨一無二名稱,容器會以該名稱建立EntityManagerFactory,並用其建立EntityManager,一個persistence.xml中可以定義多個Persistence Unit,以名稱作為區隔。

* 若是將 Persistence Unit 定義在 EJB-JAR 或 WAR 模組中,則 Persistence Unit 的可見範圍僅在該模組之中,若是定義在 EAR 中,則 Persistence Unit的可見範圍為整個應用程式

* 若不使用Annotation來標註Entity類別,則可以將對應關係定義在 orm.xml 中。



* Reference
- EJB3 Gossip: 第一個 JPA (單機客戶端)
- EJB3 Gossip: 第一個 JPA(容器管理)
- EJB3 Gossip: 在 Servlet 中直接使用 JPA
- EJB3 Gossip: 部署 JPA
- Java Persistence API
- (教學簡報分享) JPA.101 JPA 簡介與教學範例
- 開源框架:Hibernate Gossip

沒有留言:

張貼留言