JavaEE6引入的JPA2.0四大新特性詳解(轉)

   Java EE 5平臺引入了Java持久化API(Java Persistence API,JPA),它爲Java EE和Java SE應用程序提供了一個基於POJO的持久化模塊。JPA處理關係數據與Java對象之間的映射,它使對象/關係(O/R)映射標準化,JPA已經被廣泛採用,已經成爲事實上的O/R持久化企業標準。

  Java EE 6帶來了JPA的最新版本 — JSR 317:Java持久化2.0,JPA 2.0帶來了許多新特性和增強,包括

  1、對象/關係映射增強;

  2、Java持久化查詢語言增強;

  3、一種新的基於標準的查詢API;

  4、支持悲觀鎖定。

 

對象/關係映射增強

  JPA 1.0支持集合的映射,但這些集合只能包含實體,JPA 2.0增加了集合映射的基礎數據類型,如String和Integer,以及嵌入式對象的集合。JPA中的嵌入式對象是一個不能存在於它自身的對象,而是作爲父對象的一部分存在,即它的數據不是存在於它自己的表中,而是嵌入在父對象的表中。

  JPA 2.0增加了兩個支持新的集合映射的註解:@ElementCollection 和 @CollectionTable。使用@ElementCollection註解指定集合的嵌入式對象,這些集合是獨立存儲在集合表中的,使用@CollectionTable註解指定集合表的詳細信息,如它包含的列。

  下面是一個嵌入式類,表示了車輛的訪問服務,它存儲了訪問的日期,描述和費用,此外,車輛可以配備一或多個可選功能,每個功能是FeatureType類型的一個枚舉值。

 

Java代碼  收藏代碼
  1. public enum FeatureType { AC, CRUISE, PWR, BLUETOOTH, TV, ... }  
  2.         @Embeddable  
  3.         public class ServiceVisit {  
  4.         @Temporal(DATE)   
  5.         @Column(name="SVC_DATE")  
  6.          Date serviceDate;    
  7.         String workDesc;     
  8.         int cost;   
  9.     }    

 

    枚舉值和嵌入式對象可以在一個表示車輛服務歷史的實體中使用,如

 

Java代碼  收藏代碼
  1. @Entity  
  2.  public class Vehicle {  
  3.        @Id int vin;  
  4.        @ElementCollection   
  5.        @CollectionTable(name="VEH_OPTNS")  
  6.  .     @Column(name="FEAT")           
  7.        Set<FeatureType> optionalFeatures;  
  8.        @ElementCollection  
  9.        @CollectionTable(name="VEH_SVC")  
  10.        @OrderBy("serviceDate")  
  11.        List<ServiceVisit> serviceHistory;  
  12.       ...      
  13.   }    

       Vehicle實體中的第一對註解@ElementCollection 和 @CollectionTable指定FeatureType值存儲在VEH_OPTNS集合表中,第二對註解@ElementCollection 和 @CollectionTable指定ServiceVisit嵌入式對象存儲在VEH_SVC集合表中。

 

  雖然在例子中沒有顯示,@ElementCollection註解有兩個屬性:targetClass 和 fetch。targetClass屬性指定基礎類或嵌入式類的類名,如果字段或屬性是使用泛型定義的,那這兩個屬性是可選的,上面這個例子就是這樣。 Fetch屬性是可選的,它指定集合是延後檢索還是立即檢索,使用javax.persistence.FetchType常量,值分別用LAZY和 EAGER,默認情況下,集合是延後匹配的。

  JPA 2.0中還有其它許多關於對象/關係映射的增強,例如,JPA 2.0支持嵌套式嵌入,關係嵌入和有序列表,也增加了新的註解增強映射功能,通過@Access註解更靈活地支持特定的訪問類型,更多用於實體關係的映射選項,如對單向一對多關係的外鍵映射支持,通過@MapsId註解支持派生身份,支持孤體刪除。

 

 

Java持久化查詢語言增

  JPA 1.0定義了一個廣泛的Java持久化查詢語言,使用它你可以查詢實體和它們的持久化狀態。JPA 2.0對JPQL做了大量改進,如現在可以在查詢中使用case表達式。在下面的查詢中,如果僱員的評分爲1,則通過乘以1.1對僱員的薪水進行了增長,如果評分爲2,則乘以1.05,其它評分則乘以1.01。

      UPDATE Employeee

Java代碼  收藏代碼
  1. SET e.salary =  
  2.  CASE WHEN e.rating = 1 THEN e.salary * 1.1  
  3.    WHEN e.rating = 2 THEN e.salary * 1.05  
  4.    ELSE e.salary * 1.01  
  5.  END  

      JPA 2.0也爲JPQL增加了大量新的運算符,如NULLIF和COALESCE,當數據庫使用其它非空數據解碼時,NULLIF運算符是非常有用的,使用 NULLIF,你可以在查詢中將這些值轉換爲空值,如果參數等於NULLIF,NULLIF會返回空值,否則返回第一個參數的值。

 假設薪水數據保存在employee表中,數據類型爲整數,卻掉的薪水解碼爲-9999,下面的查詢返回薪水的平均值,爲了正確地忽略卻掉的薪水,查詢使用NULLIF將-9999轉換爲空值。

   SELECT AVG(NULLIF(e.salary, -99999)) FROM Employeee

   COALESCE運算符接收一串參數,從列表中返回第一個非空值,相當於下面的case表達式

  value1 IS NOT NULL THEN value1

Java代碼  收藏代碼
  1. WHEN value2 IS NOT NULL THEN value2  
  2. WHEN value3 IS NOT NULL THEN value3  
  3. ...  
  4. ELSE NULL  END  

   COALESCE運算符接收一串參數,從列表中返回第一個非空值,相當於下面的case表達式

   SELECT Name, COALESCE(e.work_phone, e.home_phone) phone FROM Employeee

  假設employee表包括一個辦公電話號碼和家庭電話號碼列,無電話號碼的列使用空值表示。下面的查詢返回每個僱員的姓名和電話號碼,COALESCE運算符指定查詢返回辦公電話號碼,但如果爲空,則返回家庭電話號碼,如果兩者都爲空,則返回一個空值。

 JPA 2.0向JPQL增加的其它運算符是INDEX,TYPE,KEY,VALUE和ENTRY。INDEX運算符指定查詢時的排序順序,TYPE運算符選擇一個實體的類型,將查詢限制到一或多個實體類型,KEY,VALUE和ENTRY運算符是JPA 2.0中的泛化映射功能的一部分。使用KEY運算符提取映射鍵,VALUE運算符提取映射值,ENTRY運算符選擇一個映射實體。
 此外,JPA 2.0增加了選擇列表、以及集合值參數和非多態查詢中運算符的支持。

標準的API
  JPA 2.0中引入的另一個重要特性是標準的API,利用這個API可以動態地構建基於對象的查詢,本質上,這個標準API等價於面向對象的JPQL,使用它,你可以使用基於對象的方法創建查詢,不再需要JPQL使用的字符串操作。
  標準API是基於元模型的,元模型是一個提供了架構級關於持久化單元託管類的元數據的抽象模型, 元模型讓你構建強類型的查詢,它也允許你瀏覽持久化單元的邏輯結構。
  通常,一個註解處理器使用元模型生成靜態元模型類,這些類提供持久化狀態和持久化單元託管類的關係,但你可以對靜態元模型類編碼。下面是一個實體實例
    @Entity
Java代碼  收藏代碼
  1. public class Employee {  
  2.    @Id Long Id;  
  3.    String firstName;  
  4.    String lastName;  
  5.    Department dept;     
  6. }    
 下面是對應的靜態元模型類
   import javax.persistence.meta,model.SingularAttribute;
Java代碼  收藏代碼
  1. import javax.persistence.meta,model.StaticMetamodel;    
  2.   @Generated("EclipseLink JPA 2.0 Canonical Model Generation"  
  3.  @StaticMetamodel(Employee.class)     
  4.   public class Employee_ {  
  5.       public static volatile SingularAttribute<Employee, Long> id;     
  6.       public static volatileSingularAttribute<Employee, String> firstName;  
  7.       public static volatile SingularAttribute<Employee, String> lastName;  
  8.      public static volatile SingularAttribute<Employee, Department> dept;  
  9.   }    

    此外,JPA 2.0元模型API允許你動態訪問元模型,因此當你使用標準API時,既可以靜態訪問元模型類,也可以動態訪問元模型類。標準API提供了更好的靈活性,既可以使用基於對象的方法,又可以使用基於字符串的方法導航元模型,這意味着你有四種使用標準API的方法

  1、靜態使用元模型類
  2、靜態使用字符串
  3、動態使用元模型
  4、動態使用字符串
  無論你使用哪種方法,通過構造一個CriteriaQuery對象定義一個基於標準API的查詢時,使用一個工廠對象 CriteriaBuilder構造CriteriaQuery,可以從EntityManager 或 EntityManagerFactory類中獲得CriteriaBuilder。下面的代碼構造一個CriteriaQuery對象
   EntityManager em = ... ;
Java代碼  收藏代碼
  1. CriteriaBuilder cb = em.getCriteriaBuilder();  
  2. CriteriaQuery<Customer> cq = cb.createQuery(Customer.class);  

  注意CriteriaQuery對象是泛型類型,使用CriteriaBuilder 的createQuery方法創建一個CriteriaQuery,併爲查詢結果指定類型。在這個例子中,createQuery 方法的Employee.class參數指定查詢結果類型是Employee。CriteriaQuery對象和創建它們的方法是強類型的。

  接下來,爲CriteriaQuery對象指定一個或多個查詢源,查詢源表示查詢基於的實體。你創建一個查詢源,然後使用 AbstractQuery接口的from()方法將其添加到查詢。AbstractQuery接口是衆多接口中的一員,如 CriteriaQuery,From和root,它們都定義在標準API中。CriteriaQuery接口繼承AbstractQuery接口的屬性。
  from()方法的參數是實體類或EntityType實體的實例,from()方法的結果是一個Root對象,Root接口擴展From接口,它表示某個查詢的from子句中可能出現的對象。
  下面的代碼增加一個查詢源到CriteriaQuery對象
    CriteriaBuilder cb = em.getCriteriaBuilder();
Java代碼  收藏代碼
  1. CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);  
  2. Root<Employee> emp = cq.from(Employee.class);    

  當你向CriteriaQuery對象添加一個或多個查詢源後,你訪問元模型,然後構造一個查詢表達式,你如何做取決於你是以靜態方式提交查詢還是以動態方式提交查詢,以及是使用元模型還是字符串導航元模型。下面是一個使用元模型類靜態查詢的例子

    cq.select(emp);

Java代碼  收藏代碼
  1. cq.where(cb.equal(emp.get(Employee_.lastName), "Smith"));  
  2. TypedQuery<Employee> query = em.createQuery(cq);  
  3. List<Employee> rows = query.getResultList();  

      CriteriaQuery接口的select() 和 where()方法指定查詢結果返回的選擇項目。

  注意,你使用EntityManager創建查詢時,可以在輸入中指定一個CriteriaQuery對象,它返回一個TypedQuery,它是JPA 2.0引入javax.persistence.Query接口的一個擴展,TypedQuery接口知道它返回的類型。
  在元模型術語中,Employee_是對應於Employee實體類的規範化元模型類,一個規範化元模型類遵循JPA 2.0規範中描述的某些規則。例如,元模型類的名字於託管類,一般都是在託管類名字後面追加一個下劃線“_”。一個規範化元模型是一個包含靜態元模型類的元模型,這個靜態元模型對應於實體,映射的超類,以及持久化單元中的嵌入式類。實際上,這個查詢使用了規範化元模型。下面是一個完整的查詢
      EntityManager em = ... ;
Java代碼  收藏代碼
  1. CriteriaBuilder cb = em.getCriteriaBuilder();  
  2. CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);  
  3. Root<Employee> emp = cq.from(Employee.class);  
  4. cq.select(emp);  
  5. cq.where(cb.equal(emp.get(Employee_.lastName), "Smith"));  
  6. TypedQuery<Employee> query = em.createQuery(cq);  
  7. List<Employee> rows = query.getResultList();    

 下面是使用元模型API查詢的動態版本:

    EntityManager em = ... ;

Java代碼  收藏代碼
  1. CriteriaBuilder cb = em.getCriteriaBuilder();  
  2. CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);  
  3. Root<Employee> emp = cq.from(Employee.class);  
  4. EntityType<Employee> emp_ = emp.getModel();  
  5. cq.select(emp);  
  6. cq.where(cb.equal(emp.get(emp_.getSingularAttribute("lastName", String.class)),"Smith"));  
  7. TypedQuery<Employee> query=em.createQuery(cq);  
  8. List<Employee> rows=query.getResultList();    

      使用元模型API的標準查詢提供了與使用規範化元模型相同的類型,但它比基於規範化元模型的查詢更冗長。

  Root的getModel()方法返回根對應的元模型實體,它也允許運行時訪問在Employee實體中聲明的持久化屬性。

  getSingularAttribute()方法是一個元模型API方法,它返回一個持久化的單值屬性或字段,在這個例子中,它返回值爲Smith 的lastName屬性。下面是使用字符串的元數據導航查詢的靜態版本:

    EntityManager em = ... ;

Java代碼  收藏代碼
  1. CriteriaBuilder cb = em.getCriteriaBuilder();  
  2. CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);  
  3. Root<Employee> emp = cq.from(Employee.class);  
  4. cq.select(emp);  
  5. cq.where(cb.equal(emp.get("lastName"), "Smith"));  
  6. TypedQuery query = em.createQuery(cq);  
  7. List <Employee>rows = query.getResultList();    

 

  這個基於字符串的方法要相對容易使用些,但卻失去了元模型具有的類型安全。

 

 

支持悲觀鎖

  鎖是處理數據庫事務併發的一種技術,當兩個或更多數據庫事務併發地訪問相同數據時,鎖可以保證同一時間只有一個事務可以修改數據。

  鎖的方法通常有兩種:樂觀鎖和悲觀鎖。樂觀鎖認爲多個併發事務之間很少出現衝突,也就是說不會經常出現同一時間讀取或修改相同數據,在樂觀鎖中,其目標是讓併發事務自由地同時得到處理,而不是發現或預防衝突。兩個事務在同一時刻可以訪問相同的數據,但爲了預防衝突,需要對數據執行一次檢查,檢查自上次讀取數據以來發生的任何變化。

  悲觀鎖認爲事務會經常發生衝突,在悲觀鎖中,讀取數據的事務會鎖定數據,在前面的事務提交之前,其它事務都不能修改數據。

  JPA 1.0只支持樂觀鎖,你可以使用EntityManager類的lock()方法指定鎖模式的值,可以是READ或WRITE,如

   EntityManager em = ... ;

Java代碼  收藏代碼
  1. em.lock (p1, READ);  

  對於READ鎖模式,JPA實體管理器在事務提交前都會鎖定實體,檢查實體的版本屬性確定實體自上次被讀取以來是否有更新,如果版本屬性被更新了,實體管理器會拋出一個OptimisticLockException異常,並回滾事務。

 

  對於WRITE鎖模式,實體管理器執行和READ鎖模式相同的樂觀鎖操作,但它也會更新實體的版本列。

  JPA 2.0增加了6種新的鎖模式,其中兩個是樂觀鎖。JPA 2.0也允許悲觀鎖,並增加了3種悲觀鎖,第6種鎖模式是無鎖。

  下面是新增的兩個樂觀鎖模式:

  1、OPTIMISTIC:它和READ鎖模式相同,JPA 2.0仍然支持READ鎖模式,但明確指出在新應用程序中推薦使用OPTIMISTIC。

  2、OPTIMISTIC_FORCE_INCREMENT:它和WRITE鎖模式相同,JPA 2.0仍然支持WRITE鎖模式,但明確指出在新應用程序中推薦使用OPTIMISTIC_FORCE_INCREMENT。

 

  下面是新增的三個悲觀鎖模式:

  1、PESSIMISTIC_READ:只要事務讀實體,實體管理器就鎖定實體,直到事務完成鎖纔會解開,當你想使用重複讀語義查詢數據時使用這種鎖模式,換句話說就是,當你想確保數據在連續讀期間不被修改,這種鎖模式不會阻礙其它事務讀取數據。

  2、PESSIMISTIC_WRITE:只要事務更新實體,實體管理器就會鎖定實體,這種鎖模式強制嘗試修改實體數據的事務串行化,當多個併發更新事務出現更新失敗機率較高時使用這種鎖模式。

  3、PESSIMISTIC_FORCE_INCREMENT:當事務讀實體時,實體管理器就鎖定實體,當事務結束時會增加實體的版本屬性,即使實體沒有修改。

  你也可以指定新的鎖模式NONE,在這種情況下表示沒有鎖發生。

  JPA 2.0也提供了多種方法爲實體指定鎖模式,你可以使用EntityManager的lock() 和 find()方法指定鎖模式。此外,EntityManager.refresh()方法可以恢復實體實例的狀態。

  下面的代碼顯示了使用PESSIMISTIC_WRITE鎖模式的悲觀鎖

    // read

Java代碼  收藏代碼
  1.  Part p = em.find(Part.class, pId);  
  2.  // lock and refresh before update   
  3. em.refresh(p, PESSIMISTIC_WRITE);  
  4.  int pAmount = p.getAmount();  
  5.  p.setAmount(pAmount - uCount);    

  在這個例子中,它首先讀取一些數據,然後應用PESSIMISTIC_WRITE鎖,在更新數據之前調用 EntityManager.refresh()方法,當事務更新實體時,PESSIMISTIC_WRITE鎖鎖定實體,其它事務就不能更新相同的實體,直到前面的事務提交。


  更多JPA 2.0的新特性
  除了前面描述的增強和新特性外,JPA 2.0可以使用Bean驗證自動驗證實體,這意味着你可以在實體上指定一個約束,例如,實體中字段的最大長度爲15,當實體持久化,更新或移除時自動驗證字段,你可以在persistence.xml配置文件中使用元素指定自動驗證生效的週期。
發佈了106 篇原創文章 · 獲贊 29 · 訪問量 66萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章