WAS V7.0 Feature Pack for Java Persistence API 2.0 新特性介紹

CloudSpace發表於2010-10-13
李 明慧, 軟體工程師, IBM
馮 月利, 軟體工程師, IBM
喬 希, 軟體工程師, IBM

簡介: 物件 - 關係持久化是 Java EE 應用開發中的一個重要部分。Java Persistence API (JPA) 是物件 - 關係持久化的 Java EE 標準,從 Java EE 5 開始被引入。最新的 JPA 2.0(JSR-317) 規範是 Java EE 6 標準的一部分,它引入了新的物件 - 關係持久化 API,基於 JPA 1.0/1.2 作了更進一步的擴充套件和提升。Apache OpenJPA 是業界領先的開源 Java 持久化框架。WebSphere Application Server 的 JPA 實現是基於 Apache OpenJPA 的。WebSphere Application Server V7.0 的 JPA 2.0 功能部件包基於 OpenJPA 2.0.0,提供了 IBM 對 JPA 2.0 規範的實現,並增加 IBM 的增強特性,使得其能與 WebSphere Application Server 更好地整合。本文將向您系統的介紹 WebSphere Application Server V7.0 中 JPA 2.0 功能部件包的新特性。

引言

物件 / 關係持久化是很多應用開發場景中非常重要的開發需求。早在 JPA 之前,Java 領域中曾湧現出很多技術方案,旨在解決資料持久化的問題,從最早的序列化(Serialization)、JDBC、關係物件對映(ORM)、物件資料庫(ODB),再到 EJB2.x、Java 資料物件(JDO)。這些方案,除 JDO 之外都有各自的侷限性,JPA 很好地克服了這些侷限性,是持久化方案中一個不錯的選擇。表 1 中是 JPA 與其它持久化技術的比較。


表 1. JPA 與其它持久化技術的比較

序列化 JDBC ORM ODB EJB2 JDO JPA
持久化 JAVA 物件
高階物件導向對徵
事務完整性
併發性
大資料集
已有的模式
關係型或非關係型資料庫
資料查詢
嚴格標準 / 可移植性
簡單性

JPA 融合了上面提到的每一種持久化技術的優點。它的優勢是:

  • 簡單易用,簡化程式設計。通過 JPA 建立實體如同建立序列化類一樣的簡單。與 JDO 和 EJB2.x 的實體 Bean 相比較,JPA 不受現有複雜規範的限制,能夠做到簡單易用。
  • JPA 支援大資料集、資料一致性、併發性和 JDBC 的查詢能力。
  • 與物件關係軟體和物件資料庫一樣,JPA 允許使用像繼承這樣的高階物件導向技術。

需要說明的是,JPA 並不適用於所有的應用,但對於大多數應用來說,相對於其它的持久化技術,JPA 確實是一個不錯的選擇。

最初,Java™ Persistence API (JPA) 作為 Enterprise JavaBean™ (EJB) 3.0 規範的一部分被引入到了 Java Platform. Enterprise Edition (Java EE) 5 中,JPA 吸取了當前 Java 持久化技術的優點,旨在規範、簡化 Java 物件的持久化工作。在 Java EE 6 中,JPA 2.0 (JSR-317) 通過提供一些重要的 API 增強了物件 / 關係對映和持久化的能力。

Apache OpenJPA 是業界領先的開源 Java 持久化框架。WebSphere Application Server 的 JPA 實現是基於 Apache OpenJPA 的。WebSphere Application Serve r 在 V6.1 版本的 EJB 3.0 功能部件包中支援 JPA 1.0 規範。WebSphere Application Server V7.0 中支援 JPA 1.2。WebSphere Application Server V7.0 的 JPA 2.0 功能部件包基於 OpenJPA 2.0.0,提供了 IBM 對 JPA 2.0 規範的實現,並增加 IBM 的增強特性,使得其能與 WebSphere Application Server 更好地整合。WebSphere Application Server 對 JPA 的支援是向前相容的,也就是說 JPA 2.0 功能部件包同時也支援基於 JPA 1.0 和 JPA 1.2 規範開發的應用。

因為基於 OpenJPA,所以基於 OpenJPA 應用不需要做任何更改就可以執行在 WebSphere Application Server 上。此外,WebSphere Application Server 支援其與 IBM 已有特性更好地整合,包括事務、安全、叢集等。您可以在 IBM 提供的 Rational Application Developer 工具中開發自己 JPA 應用。

WebSphere Application Server V7.0 的 JPA2.0 功能部件包提供了很多 JPA 2.0 的新特性,包括以下幾個方面:

  • O/R 對映和域模型
  • 悲觀鎖的引入
  • 執行時 API 的增強,EntityManagerFactory API,EntityManager API 和 Query API
  • 通過 Criterial API 和 Metamodel 來構建基於物件的型別安全的查詢
  • 支援 Bean Validation(JSR 303),在持久化和刪除實體時進行驗證。

下面就對以上這幾方面的新特性,進行詳細的介紹:

O/R 對映和域模型

  • Access Type(@Access):在 JPA1.0 中,只能在可持久化型別(實體、embeddables、MappedSuperclass)上使用 field 訪問或 propery 訪問。當使用 field 訪問時,Persistence Provider 通過反射的方式直接訪問實體的屬性。當使用 property 訪問時,Persistence Provider 能過 getter/setter 方法來訪問實體的屬性。Access 型別在 JPA2.0 中進行了擴充套件,它可以在每一個持久化型別上或者單獨的屬性上使用,這為實體的定義以及使用實體的相關應用都來了很大的靈活性。


清單 1. Access 舉例

				  
Empoyee.java

@Access(FIELD)
@Entity public class Employee {
  @Id 
  public int id;
  String name;
  @Access(PROPERTY)
  public String getName() 
  …}


  • 在 JPA1.0 中就已經有 Embeddables 類了,JPA2.0 中 Embeddables 的定義和用法在得到了擴充套件,使其包含了 embeddables 集合、巢狀式 embeddables 和包含與其他實體關係的 embeddables;

下面是一個 embeddables 集合的舉例。因為在資料庫表中,不可能將多個值儲存在同一行中,所以需要另外一個叫做集合表的單獨的表來儲存這些集合元素。為此,引入了 CollectionTable 註釋,每一個集合表中都有一個聯合列指向包含 embeddables 的實體表,集合表的其它列就用來儲存 embeddables 的其它屬性值。


清單 2. embeddables 集合

				  
Address.java
@Embeddable
public class Address {
	@Basic
	private String street;
	@Basic
	private String city;
	@Basic
	private String state;
	@Basic
	private Integer zip;

	public Address(){
	}
//...
}
User.java
@Entity
public class User {
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private int id;

	@ElementCollection
	@CollectionTable(name="user_address")
	private Set
addresses = new HashSet
(); public User(){ } //... }

JPA2.0 中新增加了巢狀式 embeddables,即某一個 Embbedable 類用來表示另一個 Embbedable 類的狀態。


清單 3. 巢狀式 embeddables

				  

Address.java

@Embeddable
public class Address {
	@Basic
	private String street;
	@Basic
	private String city;
	@Basic
	private String state;
	@Basic
	private Integer zip;

	public Address(){
	}
//...
}

Phone.java

@Embeddable
public class Phone {
	@Basic
	private String phone_number;
	@Basic
	private String phone_type;
//...
}

ContactInfo.java

@Embeddable
public class ContactInfo {
	public ContactInfo(){	
	}
	
	@Embedded
	Address homeAddress;
	
	@Embedded
	Phone homePhone;
//...
}

User.java

@Entity
public class User {
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private int id;
	@Embedded
	ContactInfo contactInfo;
	
	public User(){
	}
//...
}


包含與其它實體關係的 embeddables,因為 embeddable 類的例項本身沒有持久化標識,與引用實體的關係是與包含 embeddable 類的實體,而不是 embeddable 類本身。


清單 4. 包含與其它實體關係的 embeddables

				  

Address.java

@Embeddable
public class Address {
	@Basic
	private String street;
	@Basic
	private String city;
	@Basic
	private String state;
	@Basic
	private Integer zip;
	
	@ManyToOne(cascade=CascadeType.ALL)
	Coordinates coordinates;

	public Address(){
	}
//...
}

Coordinates .java

@Entity
public class Coordinates {
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	int id;
	
	@Basic
	double longitude;
	@Basic
	double latitude;
	public Coordinates(){
	}
	public Coordinates(double lon, double lat){
		longitude=lon;
		latitude=lat;
	}
//...
}


  • 在 JPA1.0 中,Map 只能用來表示實體之間的關聯關係(即表之間的關係),如 @ManyToOne,@ManyToMany,而且鍵值必須是實體的屬性值。在 JPA2.0 中,Map 的鍵值還可以支援基本型別,embeddables 類,以及實體。同時引入了 @MapKeyColumn、@MapKeyClass 和 @MapKeyJoinColumn 來支援這一增強特性。

如果對映鍵值是基本型別,通過 MapKeyColumn 來指定用來對映的鍵值列。


清單 5. MapKeyColumn 舉例

				  
@Entity
public class Employee {
@Id private int id;
private String name;
private long salary;
@ElementCollection
@CollectionTable(name="EMP_PHONE")
@MapKeyColumn(name="PHONE_TYPE")
@Column(name="PHONE_NUM")
private Map phoneNumbers;
// ...
}


如果對映鍵值是實體,通過 MapKeyJoinColumn 來指定用來對映的鍵值列。


清單 6. MapKeyJoinColumn 舉例

				  
@Entiy
Public class Employee{
@Id private int id;
private String name;
…
}

@Entity
public class Department {
@Id private int id;
private String name;
// ...
@ElementCollection
@CollectionTable(name="EMP_SENIORITY")
@MapKeyJoinColumn(name="EMP_ID")
@Column(name="SENIORITY")
private Map seniorities;
// ...
}
 

如果在定義對映時沒有指定 Java 泛型型別,就必須使用 MapKeyClass 來指定對映鍵值的型別


清單 7. MapKeyClass 舉例

				  
@Entity
public class Employee {
@Id private int id;
private String name;
private long salary;
@ElementCollection(targetClass=String.class)
@CollectionTable(name="EMP_PHONE")
@MapKeyColumn(name="PHONE_TYPE")
@MapKeyClass(String.class)
@Column(name="PHONE_NUM")
private Map phoneNumbers;
// ...
}

  • Derived Identities( 派生身份 ),它使得一個實體的 ID 能從其它實體派生出來,這提供了一種 parent-to-dependent 關係。

在下面的例子中,實體 Band 的 ID 來自於 Employee,Employee 實體就是 Parent,Band 就是 dependent。


清單 8. Derived Identities 舉例

				  

@Entity
public class Employee {
@Id long empId;
String empName;
...
}

@Entity
Public class Band{
@Id Employee emp
…
}


  • Java Persistence Query language (JPQL) 擴充套件與增強

JPA1.0 定義了一個豐富的 Java 持久化查詢語言,可以用來查詢實體以及實體的持久化狀態。JPA2.0 又對 JPQL 進行了一些擴充套件,例如,可以在查詢中使用 case 表達示,在下面的例子中,使用 case 表達示來對增加員工的薪水,如果員工級別為 1. 薪水乘以 1.1,如果員工級別為 2,薪水乘以 1.05,如果員工級別為 1 和 2 以外的其它級別,. 薪水乘以 1.01。


清單 9. CASE 表達示程式碼示例

				  
  UPDATE Employee e
   SET e.salary =
      CASE WHEN e.rating = 1 THEN e.salary * 1.1
           WHEN e.rating = 2 THEN e.salary * 1.05
           ELSE e.salary * 1.01
      END

JPA2.0 還對 JPQL 增加了一系列新的運算子,如 NULLIF 和 COALESCE。當資料庫使用使用其它非 null 資料解碼時,NULLIF 是十分有用的。使用 NULLIF,可以在查詢語句中輕鬆的將非 null 值轉換成 null,如查引數與 NULLIF 相等,NULLIF 就返回 null,否則返回第一個引數的值。


清單 10. NULLIF 舉例

				  
SELECT AVG(NULLIF(e.salary, -1))
   FROM Employee e 

在上面的清單中,假定員工表中薪水是一個整型數值,如果沒有薪水值,就用 -1 替代。這個查詢返回薪水的平均值。值用 NULLIF 就可通過將 -1 轉變成 null 值,來正確的忽掉不存在的薪水值。

COALESCE 運算子用返回一系列引數中第一個非 null 的值。


清單 11. COALESCE 舉例

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

在上面的清單中,假定員工表中有一列單位電話、一列家庭電話,沒有的電話號碼用 null 來表示。上面查詢返回員工姓名和員工的電話。COALESCE 運算子指定返回單位電話,如果單位電話為 null,則返回家庭電話。如果兩者都為 null,則返回 null。

JPA2.0 還增加了其它的運算子 INDEX,TYPE,KEY,VALUE 和 ENTRY。INDEX 運算子是在指定在有序列表進行有序查詢。TYPE 運算子選擇實體型別,並且可以將查詢限制到一個或多個實體型別。KEY,VALUE 和 ENTRY 運算子是 JPA2.0 泛化對映功能的一部分,可以用 KEY 來抽取對映的鍵值,VALUE 來抽取對映的值,ENTRY 就來選取對映的元素。

除此之外,JPA2.0 還為選擇列表,集合引數值以及非多型查詢增加了運算子。

悲觀鎖的引入

JPA2.0 的另外一個亮點,就是引入了 pessimistic LockManager。JPA1.0 只支援樂觀鎖,可以通過 EntityManager 類的 lock()方法指定鎖模式的值,可以是 READ 或 WRITE。樂觀讀鎖,確保在實體的狀態從資料庫中讀出來之後,只在沒有中間插入的其它事務更改了與這個實體對應的資料庫記錄的情況下,才把更新後的實體狀態寫回資料庫。它確保對資料的更新和刪除與資料庫的當前裝態保持一致,並且不會丟失中間的修改。樂觀寫鎖就是在樂觀讀鎖的基礎上必須強制對實體的版本字斷做一個更新(增量)。

JPA2.0 有 6 種新的鎖模式,其中 3 種是悲觀鎖,2 種是樂觀鎖,還有一種鎖模式是無鎖。

新增的 3 個悲觀鎖模式:

  1. PESSIMISTIC_READ:只要事務讀實體,實體管理器就鎖定實體,直到事務完成鎖才會解開,當想保證資料在連續的讀之間不被修改時,就可以使用這種鎖模式,即這種鎖模式不會阻礙其它事務讀取資料。
  2. PESSIMISTIC_WRITE:只要事務更新實體,實體管理器就會鎖定實體,這種鎖模式強制嘗試修改實體資料的事務序列化,當多個併發更新事務出現更新失敗機率較高時使用這種鎖模式。
  3. PESSIMISTIC_FORCE_INCREMENT:當事務讀實體時,實體管理器就鎖定實體,當事務結束時會增加實體的版本屬性,即使實體沒有修改。

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


清單 12. 悲觀鎖示例

				  
 // read
 Part p = em.find(Part.class, pId);

 // lock and refresh before update
 em.refresh(p, PESSIMISTIC_WRITE);
 int pAmount = p.getAmount();
 p.setAmount(pAmount - uCount);

上面示例程式碼裡,首先讀一些資料,然後在更新資料之前,通過呼叫 EntityManager.refresh() 方法來使用 PESSIMISTIC_WRITE 鎖。PESSIMISTIC_WRITE 在事務更新資料時鎖定實體,這樣其它的事務在初始事務提交之前不能更新同一實體。

執行時 API 更新

  • EntitiyManagerFactory API,增加了對 L2 快取,Properties,Criteria API 和 Metamodel AP I 的支援;
  • EntityManager,新增對 Query API,Query Result API,Hits,Properties,LockModeType,以及 Detach 的支援;
  • Query API,新增了方法用來獲取 typed query 引數和結果,支援實時 Hints,以及鎖的 getter/setter 方法;

通過 Criteria API 構建查詢

JPA2.0 中一個重大的新特性就是引入 Metamodel 和 Criterial API 的組合。Criterial API 是用來動態構建基於物件的查詢的一套 API。本質上講,Criterial AP I 就是 JPQL 的面象對向的等價物。通過 Criterial API,就可以基於物件的方式來建立查詢,而不是像 JPQL 那樣通過字串來建立查詢語句。

Criterial API 基於 metamodel,metamodel 為持久化單元所管理的類提供了一個 schema 級的抽象模型。通過 metamodel 可以構建強型別的查詢,可以查詢持久化單元的邏輯結構。

JPA1.0 引入了 JPQL 查詢語言,這在很大程度上推動了 JPA 的流行,但是這種基於符串並使用有限語法的 JPQL 存在一些缺陷。請看下面一段簡單的程式碼示例,使用 JPQL 來查詢年薪大於 20 萬元的 Employee 列表


清單 13. 簡單的 JPQL 查詢語句

				  
EntityManager em = ...;
String jpql = "select e from Employee where e.salary > 2000000";
Query query = em.createQuery(jpql);
List result = query.getResultList();

JPQL 查詢被指定為一個字串,EntityManager 構建一個包含 JPQL 字串的查詢例項,然後查詢結果是一個無型別的 java.util.List。

但是這個例子中有一個驗證錯誤,該程式碼能夠通過編譯,但是執行時會失敗,因為 JPQL 查詢字串中有語法錯誤。


清單 14. 正確的查詢語

				  
String jpql = "select e from Employee e where e.salary > 2000000";

使用 Criteria API 可以避免這種構造查詢語句時的語法錯誤。


清單 15. 使用 Criteria API 的查詢

				  
EntityManager em = ...
QueryBuilder qb = em.getQueryBuilder();
CriteriaQuery c = qb.createQuery(Employee.class);
Root< Employee> e = c.from(Employee.class);
Predicate condition = qb.gt(e.get(Employee_.salary), 2000000);
c.where(condition);
TypedQuery< Employee> q = em.createQuery(c); 
List< Employee> result = q.getResultList();

上面的程式碼展示了 Criteria API 的核心構造和基本使用。首先獲取一個 EntityManager 例項,然後建立一個 QueryBuilder 的例項。QueryBuilder 是 CrtieriaQuery 的工廠,它用來構建 CrtieriaQuery 例項,在 CrtieriaQuery 例項上設定查詢表示式。Root 是泛型的,型別引數是表達示要計算的值的型別。QueryBuilder 還用來構建查詢表達示 Predicate condition = qb.gt(e.get(Employee_.salary), 2000000),這個方法顯示了使用強型別語言定義能檢查正確性的 API 的一個不錯的例子。因為每個查詢表達款都是泛型的,並且 API 中型別安全繼承,編譯器會對無意的比較丟擲錯誤,比如:Predicate condition = qb.gt(p.get(Employee_.salary, "xyz"));TypedQuery 結果具有相同的 Employee.class 型別,因些最終查詢結果也是帶有型別 Employee 的列表,這可以省去開發人員在遍歷生成的元素時進行強制轉換的操作,減少了 ClassCastException 執行時錯誤的產生。

在清單 7 中,有一個 Employee_.salary 這樣的構造,它是表示 Employee 的持久化屬性 salary。Employee_.salary 是 Employee_ 類中的公共靜態欄位,Employee_ 是靜態、已例項化的規範 metamodel 類,對應於原來的 Employee 實體類。

Metamodel 描述持久化類的後設資料。如果一個類按著 JPA2.0 規範精確地描述持久化實體的資料,這個元模型就是規範的,規範的元模型類是靜態的,因些它所有的成員變理都被宣告成靜態的。


清單 16. 持久化實體示例

				  
@Entity
public class Employee {
  @Id
  private long ssn;
  private string name;
  private int age;
  private float salary;

  // public gettter/setter methods
  public String getName() {...}
}


清單 17. Employee 對應的靜態規範 metamodel 類
				  
import javax.persistence.metamodel.SingularAttribute;

@javax.persistence.metamodel.StaticMetamodel(Employee.class)

public class Employee_ {
  public static volatile SingularAttribute< Employee,Long> ssn;
  public static volatile SingularAttribute< Employee,String> name;
  public static volatile SingularAttribute< Employee,Integer> age;
  public static volatile SingularAttribute< Employee,Float> salary;
}

前面提到 Criteria API 是強型別的,Criteria API 也是一種動態建立查詢的機制。它可以通過以下幾種方式來動態建立查詢 :

  • 以弱型別的方式動態構建查詢;
  • 以資料庫內建的函式作為查詢表示式擴充套件語法;
  • 在結果集中再查詢;
  • 根據模板進行查詢。

接下來分別舉例說明:

  • 以弱型別的方式動態構建查詢

Criteria API 的強型別檢查要求實列化元模型,這通常只能在開發階段實現。但是有些時候,實體是在執行時被動態選定的。Criteria API 還提供了另外一種方式,也就是通過屬性的名字來訪問實體的屬性。


清單 18. 弱型別查詢舉例

				  
Class cls =Class.forName("Employee");
Metamodel model = em.getMetamodel();
EntityType entity = model.entity(cls); 
CriteriaQuery c = cb.createQuery(cls);
Root emp = c.from(entity);
Path age = account.get("age");
c.where(cb.gt(age),50);

  • 以資料庫內建的函式作為查詢表示式擴充套件語法

動態查詢機制的一個亮點就是其語法可以擴充套件。通過使用 QueryBuilder 介面的 function() 方法來建立資料庫支援的表示式。

Expression function(String name, Class type, Expression>...args);

訪函式用於建立一個給定名稱帶有零個或多個表示式引數的表示式。應用可以通這個表示式來呼叫資料庫函式來進行查詢。例如 CURRENT_USER() 這是一個 MySQL 的內建函式用來返回使用者名稱和主機名的 UTF-8 字串。


清單 19. CriteriaQuery 中使用資料庫內建於函式

				  
CriteriaQuery q = cb.createTupleQuery();
Root c = q.from(Employee.class);
Expression currentUser =
	cb.function("CURRENT_USER", String.class, (Expression>[])null);
q.multiselect(currentUser, c.get(Employee_.Name));

  • 在結果集中再查詢

CrtiteriaQuery 可以以程式設計的方式來編輯。像選擇條件,WHERE 子句中的選擇謂詞,以及 OrderBy 子句的排序條件等都可通被編輯,以達到在結果集中進行搜尋功能。


清單 20. 結果集再查詢舉例

				  
CriteriaQuery c = cb.createQuery(Employee.class);
Root p = c.from(Employee.class);
c.orderBy(cb.asc(p.get(Employee_.name)));
List result = em.createQuery(c).getResultList();
// start editing
List rders = c.getOrderList();
List newOrders = new ArrayList(orders);
newOrders.add(cb.desc(p.get(Employee_.zipcode)));
 c.orderBy(newOrders);
List result2 = em.createQuery(c).getResultList();

  • 根據模板進行查詢

Criteria API 通過建立模板,並根據模板來進行查詢。有了給定的模板例項之後,將建立一個聯合謂詞,其中每個謂詞都是模板例項的非 null 和非預設屬性值。執行該查詢將計算謂詞以查詢所有與模板例項匹配的例項。


清單 21. 模板查詢舉例

				  
CriteriaQuery q = cb.createQuery(Employee.class);
Employee example = new Employee();
example.setSalary(10000);
example.setRating(1);
q.where(cb.qbe(q.from(Employee.class), example);

如上面例子所示,OpenJPA 的 QueryBuilder 介面擴充套件支援以下表示式:

public Predicate qbe(From, T> from, T template);

如這這個表示式根據給定模板例項的屬性值生成一個聯合謂詞。例如,這個查詢將查詢所有薪水為 10000 評級為 1 的 Employee。

Bean Validation

JPA2.0 引入 Bean Validation,Bean Validation 是由 JSR 303 規範來定義的。JPA2.0 支援通過 JSR 303 的實現在實體持久化和刪除操作之前來對實體進行驗證。


清單 22. Bean Validation 舉例

				  
@Embeddable
public class Author {
    private String firstName;
    @NotEmpty(message="lastname must not be null")
    private String lastName;
    @Size(max=30)
    private String company;
    ...
}

@Entity
public class Book {
    @NotEmpty(groups={FirstLevelCheck.class, Default.class})
    private String title;

    @Valid
    @NotNull
    @Embedded
    private Author author;
    ...
}

效能增強

JPA2.0 的另一更新就是引入的 L2 快取。JPA 中有兩個級別的快取:第一級是持久化上下文,在一個持久化上下文中,Entity Manager 會確保唯一的實體例項對應一個特定的資料庫行。第二級快取(L2)是在多個上下文之間共享實體的狀態。在 JPA1.0 中,並沒定義支援第二級的快取。

如果啟用 L2 快取的話,在持久化上下文中找不到的實體,就會從 L2 快取中找,如果找到,就會從 L2 快取中裝載。這一行為,通過 CacheModes 和快取元素來控制。


圖 1. JPA 的第一級與第二級快取
圖 1 JPA 的第一級與第二級快取-1

圖 1 JPA 的第一級與第二級快取-2

使用 JPA2.0 的 L2 快取的好處:

  • 對於已經裝載的實體,可以避免對資料庫的再次訪問;
  • 可以快速讀取頻繁訪問的未改變的實體。

就像一枚硬幣有正面,也有反正一樣,L2 快取存在著缺點:

  • 大量的實體物件會帶來記憶體的消耗;
  • 更新物件後會來陳舊的資料;
  • 可會出現同時寫操作;
  • 對於頻繁同時更新的實體會帶來擴充套件性方面的問題

但是快取卻是 Java EE 方面有效的提高效能的設計模式,下面的圖表明,隨著使用者的增加,L2 快取所帶來的效能的提高。


圖 2. L2 快取效能提示例
圖 2. L2 快取效能提示例

WebSphere 的 JPA 解決方案基於 OpenJPA,但是同時還對 OpenJAP 進行了擴充套件,包括了與 IBM Data Studio Pure Query 的整合和 WebSphere eXtreme Scale 整合。


圖 3. WebSphere JPA 架構
圖 3 WebSphere JPA 架構

JPA2.0 功能部件包與 IBM Data Studio Pure Query 的整合

IBM pureQuery 是一個高效能的 Java 資料訪問平臺,可以簡化資料訪問的開發,優化,保護和管理。PureQuery 提供一可以用來替代 JDBC 的 API 來訪問資料庫。

JPA 使用 pureQuery 的靜態 SQL,SQL 監視和問題診斷以及異構環境下批處理更新操作。

與 JPA 功能部件整合在一起的 pureQury,允許其它資料庫(如 Oracle)使用 wsdb2gen 功能,使用 pureQuery 的 sql 監控和問題診斷功能,這樣可像 DB2 使用者一樣來調優應用的 SQL 使用。同時它也允許其它的 DBRA 資料庫(如 Informix)使用者使 heterogeneous batching support,這使得他們體驗到像 DB2 使用者一樣效能特性。

在瞭解 IBM WebSphere eXtreme Scale 之前,我們簡單介紹下延遲寫快取系統。快取系統是位於資料庫和應用資料訪問之間的中間系統,對於一個延遲寫快取,寫操作不會立即反應到儲存庫中,快取會將被更新的資料標記為髒資料,只有在資料因為某些原因(如更新機制)被從快取中移除的時候,寫操作才會在儲存庫上執行。因此,如果在一個延遲寫快取上產生一個讀取失敗時,會激發兩個動作:一個執行緒到儲存庫獲取資料,一個執行緒將快取中的髒資料更新到儲存庫(用於釋放空間留給新的資料)。當然,可以顯式的要求快取將髒資料更新到儲存庫。下圖顯示了延遲寫快取系統所處的地位和工作流程。


圖 5. 延遲寫快取系統所處的地位和工作流程
圖 5. 延遲寫快取系統所處的地位和工作流程

快取系統的優點是能夠顯著提高系統事務率(Transactions Per Second, TPS),同時資料庫負載降低,但對於 server 而言,需要增加額外的記憶體消耗,並且需要增加額外執行緒。

使用外部的資料快取系統能夠給 JPA 資料訪問操作效能帶來大幅度提升,下面我們以 IBM WebSphere eXtreme Scale(以下簡稱 WXS) 為例,介紹如何配置 OpenJPA 上的外部快取。

OpenJPA 提供多種快取機制,如 DataCache 和 QueryCache,DataCache 表示對資料進行快取,QueryCach e 表示對查詢結果進行快取,它們都可以通過在 persistence.xml 中的配置進行啟用 / 停止,可選值為 true、false 或第三方實現類路徑,同時可以設定相應引數。簡單的情況下,可以使用

表示啟用資料快取。

還可以在 value 處配置相應引數,如

用於指定快取資料量大小。

WXS 提供了專門支援 OpenJPA DataCache 和 QueryCache 的實現,在 persistence.xml 中使用如下配置,即可啟用 WXS 針對 OpenJPA 的 QueryCache 快取實現

對於一般情況而言,只需做出上述配置,即可滿足系統需求,但在一些需要特殊定製的環境中,WXS 也提供了增強的擴充套件配置方式,即在 META-INF 目錄下,使用自定義的 OpenJPA ObjectGrid XML 豐富擴充套件配置。有以下三類配置檔案型別可以供使用者使用:openjpa-objectGrid.xml ( ObjectGrid 配置檔案 ), openjpa-objectGridDeployment.xml ( 部署策略配置 ),和 openjpa-objectGrid-client-override.xml ( 客戶端的 ObjectGrid 覆蓋配置 ),使用者可以根據需求使用任何一個或全部配置檔案。

對於 EMBEDDED 和 EMBEDDED_PARTITION 型別,可以根據需求使用任何一個或全部配置檔案

對於 REMOTE ObjectGrid 快取而言,ObjectGrid cache 不會建立動態 ObjectGrid,而是從 catalog service 獲取客戶端的 ObjectGrid,因此此時使用者只能配置 openjpa-objectGrid-client-override.xml 來覆蓋原有配置。如果希望進一步瞭解 WebSphere eXtreme Scale 的詳細資訊,請參考 WebSphere eXtreme Scale 相關文章。

JPA2.0 規範使得 EJB 中實體 bean 的開發變得更加簡單,靈活。本文介紹了 JPA2.0 功能部件的最主要的幾個新特性。需要注意的是 JPA 2.0 功能部件包是同 OSGi 應用功能部部件包一起釋出的並且可以同時安裝的,但是二者之間沒有必然的聯絡,也可以進行單獨安裝。

原文連結:http://www.ibm.com/developerworks/cn/websphere/library/techarticles/1010_limh_was7fp/1010_limh_was7fp.html

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/14789789/viewspace-675839/,如需轉載,請註明出處,否則將追究法律責任。

相關文章