内容


IBM WebSphere Application Server V7.0 Feature Pack for Java Persistence API 2.0 新特性介绍

Comments

引言

对象 / 关系持久化是很多应用开发场景中非常重要的开发需求。早在 JPA 之前,Java 领域中曾涌现出很多技术方案,旨在解决数据持久化的问题,从最早的序列化(Serialization)、JDBC、关系对象映射(ORM)、对象数据库(ODB),再到 EJB2.x、Java 数据对象(JDO)。这些方案,除 JDO 之外都有各自的局限性,JPA 很好地克服了这些局限性,是持久化方案中一个不错的选择。表 1 中是 JPA 与其它持久化技术的比较。

表 1. JPA 与其它持久化技术的比较
序列化JDBCORM 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 JPA 2.0 功能部件包的新特性

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<Address> addresses = new HashSet<Address>();
	
	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<String, String> 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<Employee, Integer> 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<Employee> 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<Employee> 是泛型的,类型参数是表达示要计算的值的类型。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<Employee> cls =Class.forName("Employee");
Metamodel model = em.getMetamodel();
EntityType<Employee> entity = model.entity(cls); 
CriteriaQuery<Employee> c = cb.createQuery(cls);
Root<Employee> emp = c.from(entity);
Path<Integer> age = account.<Integer>get("age");
c.where(cb.gt(age),50);
  • 以数据库内置的函数作为查询表达式扩展语法

动态查询机制的一个亮点就是其语法可以扩展。通过使用 QueryBuilder 接口的 function() 方法来创建数据库支持的表达式。

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

访函数用于创建一个给定名称带有零个或多个表达式参数的表达式。应用可以通这个表达式来调用数据库函数来进行查询。例如 CURRENT_USER() 这是一个 MySQL 的内置函数用来返回用户名和主机名的 UTF-8 字符串。

清单 19. CriteriaQuery 中使用数据库内置于函数
CriteriaQuery<Tuple> q = cb.createTupleQuery();
Root<Employee> c = q.from(Employee.class);
Expression<String> currentUser =
	cb.function("CURRENT_USER", String.class, (Expression<?>[])null);
q.multiselect(currentUser, c.get(Employee_.Name));
  • 在结果集中再查询

CrtiteriaQuery 可以以编程的方式来编辑。像选择条件,WHERE 子句中的选择谓词,以及 OrderBy 子句的排序条件等都可通被编辑,以达到在结果集中进行搜索功能。

清单 20. 结果集再查询举例
CriteriaQuery<Employee> c = cb.createQuery(Employee.class);
Root<Employee> p = c.from(Employee.class);
c.orderBy(cb.asc(p.get(Employee_.name)));
List<Employee> result = em.createQuery(c).getResultList();
// start editing
List<Order> orders = c.getOrderList();
List<Order> newOrders = new ArrayList<Order>(orders);
newOrders.add(cb.desc(p.get(Employee_.zipcode)));
 c.orderBy(newOrders);
List<Employee> result2 = em.createQuery(c).getResultList();
  • 根据模板进行查询

Criteria API 通过创建模板,并根据模板来进行查询。有了给定的模板实例之后,将创建一个联合谓词,其中每个谓词都是模板实例的非 null 和非默认属性值。执行该查询将计算谓词以查找所有与模板实例匹配的实例。

清单 21. 模板查询举例
CriteriaQuery<Employee> 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 <T> 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 的第一级与第二级缓存-1
图 1 JPA 的第一级与第二级缓存-2
图 1 JPA 的第一级与第二级缓存-2

使用 JPA2.0 的 L2 缓存的好处:

  • 对于已经装载的实体,可以避免对数据库的再次访问;
  • 可以快速读取频繁访问的未改变的实体。

就像一枚硬币有正面,也有反正一样,L2 缓存存在着缺点:

  • 大量的实体对象会带来内存的消耗;
  • 更新对象后会来陈旧的数据;
  • 可会出现同时写操作;
  • 对于频繁同时更新的实体会带来扩展性方面的问题

但是缓存却是 Java EE 方面有效的提高性能的设计模式,下面的图表明,随着用户的增加,L2 缓存所带来的性能的提高。

图 2. L2 缓存性能提示例
图 2. L2 缓存性能提示例
图 2. L2 缓存性能提示例

WebSpehere Application Server 对 OpenJPA 的扩展与增强

WebSphere 的 JPA 解决方案基于 OpenJPA,但是同时还对 OpenJAP 进行了扩展,包括了与 IBM Data Studio Pure Query 的集成和 WebSphere eXtreme Scale 集成。

图 3. WebSphere JPA 架构
图 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 用户一样性能特性。

JPA2.0 功能部件包与 WebSphere eXtreme Scale 的集成

在了解 IBM WebSphere eXtreme Scale 之前,我们简单介绍下延迟写缓存系统。缓存系统是位于数据库和应用数据访问之间的中间系统,对于一个延迟写缓存,写操作不会立即反应到存储库中,缓存会将被更新的数据标记为脏数据,只有在数据因为某些原因(如更新机制)被从缓存中移除的时候,写操作才会在存储库上执行。因此,如果在一个延迟写缓存上产生一个读取失败时,会激发两个动作:一个线程到存储库获取数据,一个线程将缓存中的脏数据更新到存储库(用于释放空间留给新的数据)。当然,可以显式的要求缓存将脏数据更新到存储库。下图显示了延迟写缓存系统所处的地位和工作流程。

图 5. 延迟写缓存系统所处的地位和工作流程
图 5. 延迟写缓存系统所处的地位和工作流程
图 5. 延迟写缓存系统所处的地位和工作流程

缓存系统的优点是能够显著提高系统事务率(Transactions Per Second, TPS),同时数据库负载降低,但对于 server 而言,需要增加额外的内存消耗,并且需要增加额外线程。

使用外部的数据缓存系统能够给 JPA 数据访问操作性能带来大幅度提升,下面我们以 IBM WebSphere eXtreme Scale(以下简称 WXS) 为例,介绍如何配置 OpenJPA 上的外部缓存。

OpenJPA 提供多种缓存机制,如 DataCache 和 QueryCache,DataCache 表示对数据进行缓存,QueryCach e 表示对查询结果进行缓存,它们都可以通过在 persistence.xml 中的配置进行启用 / 停止,可选值为 true、false 或第三方实现类路径,同时可以设置相应参数。简单的情况下,可以使用

<property name="openjpa.DataCache" value="true"/>

表示启用数据缓存。

还可以在 value 处配置相应参数,如

<property name="openjpa.DataCache" value="true(CacheSize=10000)"/>

用于指定缓存数据量大小。

WXS 提供了专门支持 OpenJPA DataCache 和 QueryCache 的实现,在 persistence.xml 中使用如下配置,即可启用 WXS 针对 OpenJPA 的 QueryCache 缓存实现

<property name="openjpa.QueryCache" value="com.ibm.websphere.objectgrid.openjpa.ObjectGridQueryCache()"/>

对于一般情况而言,只需做出上述配置,即可满足系统需求,但在一些需要特殊定制的环境中,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 应用功能部部件包一起发布的并且可以同时安装的,但是二者之间没有必然的联系,也可以进行单独安装。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=WebSphere
ArticleID=550056
ArticleTitle=IBM WebSphere Application Server V7.0 Feature Pack for Java Persistence API 2.0 新特性介绍
publish-date=10112010