JPA 2.0 中的动态类型安全查询

[复制链接]
查看11 | 回复5 | 2007-1-24 12:56:49 | 显示全部楼层 |阅读模式
[size=0.76em]自从 JPA 于 2006 年首次被引入之后,它就得到了 Java 开发社区的广泛支持。该规范的下一个主要更新 —— 2.0 版本 (JSR 317) —— 将在 2009 年年底完成(见 参考资料)。JPA 2.0 引入的关键特性之一就是 Criteria API,它为 Java 语言带来了一种独特的能力:开发一种 Java 编译器可以在运行时验证其正确性的查询。Criteria API 还提供一个能够在运行时动态地构建查询的机制。[size=0.76em]本文将介绍 Criteria API 和与之密切相关的 元模型(metamodel)概念。您将学习如何使用 Criteria API 开发 Java 编译器能够检查其正确性的查询,从而减少运行时错误,这种查询优于传统的基于字符串的 Java Persistence Query Language (JPQL) 查询。借助使用数据库函数或匹配模板实例的样例查询,我将演示编程式查询构造机制的强大威力,并将其与使用预定义语法的 JPQL 查询进行对比。本文假设您具备基础的 Java 语言编程知识,并了解常见的 JPA 使用,比如 EntityManagerFactory或EntityManager。[size=0.76em]JPQL 查询有什么缺陷?[size=0.76em]JPA 1.0 引进了 JPQL,这是一种强大的查询语言,它在很大程度上导致了 JPA 的流行。不过,基于字符串并使用有限语法的 JPQL 存在一些限制。要理解 JPQL 的主要限制之一,请查看清单 1 中的简单代码片段,它通过执行 JPQL 查询选择年龄大于 20 岁的 Person列表:
清单 1. 一个简单(并且错误)的 JPQL 查询
EntityManagerem = ...;String jpql = "select p from Person where p.age > 20";Queryquery = em.createQuery(jpql);List result = query.getResultList();
回复

使用道具 举报

千问 | 2007-1-24 12:56:49 | 显示全部楼层
[size=0.76em]持久化域的元模型[size=0.76em]讨论 清单 2时指出了一个不常见的构造:Person_.age,它表示 Person的持久化属性 age。清单 2使用 Person_.age形成一个路径表达式,它通过 p.get(Person_.age)从 Root表达式 p导航而来。Person_.age是 Person_类中的公共静态字段,Person_是 静态、已实例化的规范元模型类,对应于原来的 Person实体类。[size=0.76em]元模型类描述持久化类的元数据。如果一个类安装 JPA 2.0 规范精确地描述持久化实体的元数据,那么该元模型类就是 规范的。规范的元模型类是 静态的,因此它的所有成员变量都被声明为 静态的(也是 public的)。Person_.age是静态成员变量之一。您可以在开发时在源代码中生成一个具体的 Person_.java来 实例化一个规范类。实例化之后,它就可以在编译期间以强类型的方式引用 Person的持久化属性。[size=0.76em]这个 Person_metamodel 类是引用 Person的元信息的一种代替方法。这种方法类似于经常使用(有人可能认为是滥用)的 Java Reflection API,但概念上有很大的不同。您可以使用反射获得关于 java.lang.Class的实例的元信息,但是不能以编译器能够检查的方式引用关于 Person.class的元信息。例如,使用反射时,您将这样引用 Person.class中的 age字段: Field field = Person.class.getField("age");
回复

使用道具 举报

千问 | 2007-1-24 12:56:49 | 显示全部楼层
规范和非规范
尽管 JPA 2.0 规范规定了规范的静态元模型类的精确样式(包括元模型类的完整限定名及其静态字段的名称),应用程序也能够编写这些元模型类。如果应用程序开发人员编写元模型类,这些类就称为 非规范元模型。现在,关于非规范元模型的规范还不是很详细,因此对非规范元模型的支持不能在 JPA 提供者之间移植。您可能已经注意到,公共静态字段仅在规范元模型中 声明,而没有初始化。声明之后就可以在开发 CriteriaQuery时引用这些字段。但是,必须在运行时给它们赋值才有意义。尽管为规范元模型的字段赋值是 JPA 提供者的责任,但非规范元模型则不存在这一要求。使用非规范元模型的应用程序必须依赖于特定供应商机制,或开发自己的机制来在运行时初始化元模型属性的字段值。
代码生成和可用性
自动生成源代码通常让人担心。为规范元模型生成源代码更是如此。生成的类在开发期间使用,构建 CriteriaQuery的其他代码部分直接在运行时引用它们,从而留下一些可用性问题:
生成的源代码文件应该在初始源代码所在的目录中,还是在一个独立的或与输出目录相关的目录中?
是否应该在版本控制配置管理系统中检查源代码文件?
如何维护初始 Person实体定义及其规范 Person_元模型之间的对应关系?例如,如果要编辑 Person.java 以添加其他持久化属性,或通过重构重命名持久化属性,那么应该怎么办呢?
在撰写本文时,这些问题还没有确切的答案。
注释处理和元模型生成
如果您有许多持久化实体,您将倾向于不亲自编写元模型类,这是很自然的事情。持久化提供者 应该为您生成这些元模型类。在规范中没有强制规定这种工具或生成机制,但是 JPA 之间已经私下达成共识,他们将使用在 Java 6 编译器中集成的 Annotation Processor 工具生成规范元模型。Apache OpenJPA 提供一个工具来生成这些元模型类,其生成方式有两种,一是在您为持久化实体编译源代码时隐式地生成,二是通过显式地调用脚本生成。在 Java 6 以前,有一个被广泛使用的称为 apt的 Annotation Processor 工具,但在 Java 6 中,编译器和 Annotation Processor 的合并被定义为标准的一部分。
要像持久化提供者一样在 OpenJPA 中生成这些元模型类,仅需在编译器的类路径中使用 OpenJPA 类库编译 POJO 实体:
$ javac domain/Person.java
将生成规范元模型 Person_类,它将位于 Person.java 所在的目录,并且作为该编译的一部分。

回复

使用道具 举报

千问 | 2007-1-24 12:56:49 | 显示全部楼层
编写类型安全的查询
到目前为止,我已经构建了 CriteriaQuery的组件和相关的元模型类。现在,我将展示如何使用 Criteria API 开发一些查询。
函数表达式
函数表达式将一个函数应用到一个或多个输入参数以创建新的表达式。函数表达式的类型取决于函数的性质及其参数的类型。输入参数本身可以是表达式或文本值。编译器的类型检查规则与 API 签名结合确定什么是合法输入。
考虑一个对输入表达式应用平均值的单参数表达式。CriteriaQuery选择所有 Account的平均余额,如清单 5 所示:
清单 5. CriteriaQuery中的函数表达式
CriteriaQuery c = cb.createQuery(Double.class);
Root a = c.from(Account.class);
c.select(cb.avg(a.get(Account_.balance)));
等效的 JPQL 查询为:
String jpql = "select avg(a.balance) from Account a";
在 清单 5中,QueryBuilder工厂(由变量 cb表示)创建一个 avg()表达式,并将其用于查询的 select()子句。
Fluent API
如这个例子所示,Criteria API 方法经常返回可以直接在相关方法中使用的类型,从而提供了一种称为 Fluent API 的流行编程风格。
该查询表达式是一个构建块,可以通过组装它为查询定义最后的选择谓词。清单 6中的例子显示了通过导航到 Account的余额创建的 Path表达式,然后 Path表达式被用作两个二进制函数表达式(greaterThan()和 lessThan())的输入表达式,这两个表达式的结果都是一个布尔表达式或一个谓词。然后,通过 and()操作合并谓词以形成最终的选择谓词,查询的 where()子句将计算该谓词:
清单 6. CriteriaQuery中的 where()谓词
CriteriaQuery c = cb.createQuery(Account.class);
Root account = c.from(Account.class);
Path[I] balance = account.get(Account_.balance);
c.where(cb.and
(cb.greaterThan(balance, 100),
cb.lessThan(balance), 200)));
等效的 JPQL 查询为:
"select a from Account a where a.balance>100 and a.balance c = cb.createQuery(Account.class);
Root account = c.from(Account.class);
Path owner = account.get(Account_.owner);
Path name = owner.get(Person_.name);
c.where(cb.in(name).value("X").value("Y").value("Z"));
这个例子通过两个步骤从 Account进行导航,创建一个表示帐户所有者的名称的路径。然后,它创建一个使用路径表达式作为输入的 in()表达式。in()表达式计算它的输入表达式是否等于它的参数之一。这些参数通过 value()方法在 In表达式上指定,In的签名如下所示:
In value(T value);
注意如何使用 Java 泛型指定仅对值的类型为 T的成员计算 In表达式。因为表示 Account所有者的名称的路径表达式的类型为 String,所以与值为 String类型的参数进行比较才有效,String值参数可以是字面量或计算结果为 String的另一个表达式。
将 清单 7中的查询与等效(正确)的 JPQL 进行比较:
"select a from Account a where a.owner.name in ('X','Y','Z')";
在 JPQL 中的轻微疏忽不仅不会被编辑器检查到,它还可能导致意外结果。例如:
"select a from Account a where a.owner.name in (X, Y, Z)";
连接关系
尽管 清单 6和 清单 7中的例子将表达式用作构建块,查询都是基于一个实体及其属性之上的。但是查询通常涉及到多个实体,这就要求您将多个实体 连接起来。CriteriaQuery通过 类型连接表达式连接两个实体。类型连接表达式有两个类型参数:连接源的类型和连接目标属性的可绑定类型。例如,如果您想查询有一个或多个 PurchaseOrder没有发出的 Customer,则需要通过一个表达式将 Customer连接到 PurchaseOrder,其中 Customer有一个名为 orders类型为 java.util.Set的持久化属性,如清单 8 所示:
清单 8. 连接多值属性
CriteriaQuery q = cb.createQuery(Customer.class);
Root c = q.from(Customer.class);
SetJoin o = c.join(Customer_.orders);
连接表达式从根表达式 c创建,持久化属性 Customer.orders由连接源(Customer)和 Customer.orders属性的可绑定类型进行参数化,可绑定类型是 PurchaseOrder而 不是已声明的类型 java.util.Set。此外还要注意,因为初始属性的类型为 java.util.Set,所以生成的连接表达式为 SetJoin,它是专门针对类型被声明为 java.util.Set的属性的 Join。类似地,对于其他受支持的多值持久化属性类型,该 API 定义 CollectionJoin、ListJoin和 MapJoin。(图 1显示了各种连接表达式)。在 清单 8的第 3 行不需要进行显式的转换,因为 CriteriaQuery和 Metamodel API 通过覆盖 join()的方法能够识别和区分声明为 java.util.Collection或 List或者 Set或 Map的属性类型。
在查询中使用连接在连接实体上形成一个谓词。因此,如果您想要选择有一个或多个未发送 PurchaseOrder的 Customer,可以通过状态属性从连接表达式 o进行导航,然后将其与 DELIVERED状态比较,并否定谓词:
Predicatep = cb.equal(o.get(PurchaseOrder_.status), Status.DELIVERED)
.negate();
创建连接表达式需要注意的一个地方是,每次连接一个表达式时,都会返回一个新的表达式,如清单 9 所示:
清单 9. 每次连接创建一个唯一的实例
SetJoin o1 = c.join(Customer_.orders);
SetJoin o2 = c.join(Customer_.orders);
asserto1 == o2;
清单 9中对两个来自相同表达式 c的连接表达式的等同性断言将失败。因此,如果查询的谓词涉及到未发送并且值大于 $200 的 PurchaseOrder,那么正确的构造是将 PurchaseOrder与根 Customer表达式连接起来(仅一次),把生成的连接表达式分配给本地变量(等效于 JPQL 中的范围变量),并在构成谓词时使用本地变量。
使用参数
回顾一下本文初始的 JPQL 查询(正确那个):
String jpql = "select p from Person p where p.age > 20";
尽管编写查询时通常包含常量文本值,但这不是一个良好实践。良好实践是参数化查询,从而仅解析或准备查询一次,然后再缓存并重用它。因此,编写查询的最好方法是使用命名参数:
String jpql = "select p from Person p where p.age > :age";
参数化查询在查询执行之前绑定参数的值:
Queryquery = em.createQuery(jpql).setParameter("age", 20);
List result = query.getResultList();
在 JPQL 查询中,查询字符串中的参数以命名方式(前面带有冒号,例如 :age)或位置方式(前面带有问号,例如 ?3)编码。在 CriteriaQuery中,参数本身就是查询表达式。与其他表达式一样,它们是强类型的,并且由表达式工厂(即 QueryBuilder)构造。然后,可以参数化 清单 2中的查询,如清单 10 所示:
清单 10. 在 CriteriaQuery中使用参数
ParameterExpression[I] age = qb.parameter(Integer.class);
Predicatecondition = qb.gt(p.get(Person_.age), age);
c.where(condition);
TypedQuery q = em.createQuery(c);
List result = q.setParameter(age, 20).getResultList();
比较该参数使用和 JPQL 中的参数使用:参数表达式被创建为带有显式类型信息 Integer,并且被直接用于将值 20绑定到可执行查询。额外的类型信息对减少运行时错误十分有用,因为阻止参数与包含不兼容类型的表达式比较,或阻止参数与不兼容类型的值绑定。JPQL 查询的参数不能提供任何编译时安全。
清单 10中的例子显示了一个直接用于绑定的未命名表达式。还可以在构造参数期间为参数分配第二个名称。对于这种情况,您可以使用这个名称将参数值绑定到查询。不过,您不可以使用位置参数。线性 JPQL 查询字符串中的整数位置有一定的意义,但是不能在概念模型为查询表达式树的 CriteriaQuery上下文中使用整数位置。
JPA 查询参数的另一个有趣方面是它们没有内部值。值绑定到可执行查询上下文中的参数。因此,可以合法地从相同的 CriteriaQuery创建两个独立可执行的查询,并为这些可执行查询的相同参数绑定两个整数值。
预测结果
您已经看到 CriteriaQuery在执行时返回的结果已经在 QueryBuilder构造 CriteriaQuery时指定。查询的结果被指定为一个或多个 预测条件。可以通过两种方式之一在 CriteriaQuery接口上指定预测条件:
CriteriaQuery select(Selection selection);
CriteriaQuery multiselect(Selection... selections);
最简单并且最常用的预测条件是查询候选类。它可以是隐式的,如清单 11 所示:
清单 11. CriteriaQuery默认选择的候选区段
CriteriaQuery q = cb.createQuery(Account.class);
Root account = q.from(Account.class);
List accounts = em.createQuery(q).getResultList();
在 清单 11中,来自 Account的查询没有显式地指定它的选择条件,并且和显式地选择的候选类一样。清单 12 显示了一个使用显式选择条件的查询:
清单 12. 使用单个显式选择条件的 CriteriaQuery
CriteriaQuery q = cb.createQuery(Account.class);
Root account = q.from(Account.class);
q.select(account);
List accounts = em.createQuery(q).getResultList();
如果查询的预测结果不是候选持久化实体本身,那么可以通过其他几个构造方法来生成查询的结果。这些构造方法包含在 QueryBuilder接口中,如清单 13 所示:
清单 13. 生成查询结果的方法
CompoundSelection construct(Class result, Selection... terms);
CompoundSelection array(Selection... terms);
CompoundSelection tuple(Selection... terms);
清单 13中的方法构建了一个由其他几个可选择的表达式组成的预测条件。construct()方法创建给定类参数的一个实例,并使用来自输入选择条件的值调用一个构造函数。例如,如果 CustomerDetails —一个非持久化实体 —有一个接受 String和 int参数的构造方法,那么 CriteriaQuery可以通过从选择的 Customer —一个持久化实体 —实例的名称和年龄创建实例,从而返回 CustomerDetails作为它的结果,如清单 14 所示:
清单 14. 通过 construct()将查询结果包放入类的实例
CriteriaQuery q = cb.createQuery(CustomerDetails.class);
Root c = q.from(Customer.class);
q.select(cb.construct(CustomerDetails.class,

c.get(Customer_.name), c.get(Customer_.age));
可以将多个预测条件合并在一起,以组成一个表示 Object[]或 Tuple的复合条件。清单 15 显示了如何将结果包装到 Object[]中:
清单 15. 将结果包装到 Object[]
CriteriaQuery q = cb.createQuery(Object[].class);
Root c = q.from(Customer.class);
q.select(cb.array(c.get(Customer_.name), c.get(Customer_.age));
List result = em.createQuery(q).getResultList();
这个查询返回一个结果列表,它的每个元素都是一个长度为 2 的 Object[],第 0 个数组元素为 Customer的名称,第 1 个数组元素为 Customer的年龄。
Tuple是一个表示一行数据的 JPA 定义接口。从概念上看,Tuple是一个 TupleElement列表 —其中 TupleElement是源自单元和所有查询表达式的根。包含在 Tuple中的值可以被基于 0 的整数索引访问(类似于熟悉的 JDBC 结果),也可以被 TupleElement的别名访问,或直接通过 TupleElement访问。清单 16 显示了如何将结果包装到 Tuple中:
清单 16. 将查询结果包装到 Tuple
CriteriaQuery q = cb.createTupleQuery();
Root c = q.from(Customer.class);
TupleElement tname = c.get(Customer_.name).alias("name");
q.select(cb.tuple(tname, c.get(Customer_.age).alias("age");
List result = em.createQuery(q).getResultList();
String name = result.get(0).get(name);
String age= result.get(0).get(1);
嵌套限制
从理论上讲,可以通过嵌套 Tuple等条件(它的元素本身为 Object[]或 Tuple)来构成复杂的结果。不过,JPA 2.0 规范禁止此类嵌套。multiselect()的输入条件不能是数组或值为二元组的复合条件。允许作为 multiselect()参数的唯一复合条件由 construct()方法创建(该方法仅表示一个元素)。
不过,OpenJPA 没有限制在一个复合选择条件中嵌套其他的复合选择条件。
这个查询返回一个结果列表,它的每个元素都是一个 Tuple。反过来,每个二元组都带有两个元素 —可以被每个 TupleElement的索引或别名(如果有的话)访问,或直接被 TupleElement访问。清单 16中需要注意的两点是 alias()的使用,它是将一个名称绑定到查询表达式的一种方式(创建一个新的副本),和 QueryBuilder上的 createTupleQuery()方法,它仅是 createQuery(Tuple.class)的代替物。
这些能够改变结果的方法的行为和在构造期间被指定为 CriteriaQuery的类型参数结果共同组成 multiselect()方法的语义。这个方法根据最终实现结果的 CriteriaQuery的结果类型解释它的输入条件。要像 清单 14一样使用 multiselect()构造 CustomerDetails实例,您需要将 CriteriaQuery的类型指定为 CustomerDetails,然后使用将组成 CustomerDetails构造方法的条件调用 multiselect(),如清单 17 所示:
清单 17. 基于结果类型的 multiselect()解释条件
CriteriaQuery q = cb.createQuery(CustomerDetails.class);
Root c = q.from(Customer.class);
q.multiselect(c.get(Customer_.name), c.get(Customer_.age));
因为查询结果类型为 CustomerDetails,multiselect()将其预测条件解释为 CustomerDetails构造方法参数。如将查询指定为返回 Tuple,那么带有相同参数的 multiselect()方法将创建 Tuple实例,如清单 18 所示:
清单 18. 使用 multiselect()方法创建 Tuple实例
CriteriaQuery q = cb.createTupleQuery();
Root c = q.from(Customer.class);
q.multiselect(c.get(Customer_.name), c.get(Customer_.age));
如果以 Object作为结果类型或没有指定类型参数时,multiselect()的行为会变得更加有趣。在这些情况中,如果 multiselect()使用单个输入条件,那么返回值将为所选择的条件。但是如果 multiselect()包含多个输入条件,结果将得到一个 Object[]。
回复

使用道具 举报

千问 | 2007-1-24 12:56:49 | 显示全部楼层
高级特性
到目前为止,我主要强调了 Criteria API 的强类型,以及它如何帮助减少出现在基于字符串 JPQL 查询中的语义错误。Criteria API 还是以编程的方式构建查询的机制,因此通常被称为 动态查询 API。编程式查询构造 API 的威力是无穷的,但它的利用还取决于用户的创造能力。我将展示 4 个例子:
使用弱类型的 API 构建动态查询
使用数据库支持的函数作为查询表达式来扩展语法
编辑查询实现 “在结果中搜索” 功能
根据例子进行查询 —数据库社区熟悉的模式
弱类型和动态查询构建
Criteria API 的强类型检查基于开放期间的实例化元模型类的可用性。不过,在某些情况下,选择的实体仅能够在运行时决定。为了支持这种用法,Criteria API 方法提供一个并列版本,其中持久化属性通过它们的名称进行引用(类似于 Java Reflection API),而不是引用实例化静态元模型属性。该 API 的这个并列版本可以通过牺牲编译时类型检查来真正地支持动态查询构造。清单 19 使用弱类型 API 重新编写了 清单 6中的代码:
清单 19. 弱类型查询
Class cls =Class.forName("domain.Account");
Metamodelmodel = em.getMetamodel();
EntityType entity = model.entity(cls);
CriteriaQuery c = cb.createQuery(cls);
Root account = c.from(entity);
Path[I] balance = account.[I]get("balance");
c.where(cb.and
(cb.greaterThan(balance, 100),
cb.lessThan(balance), 200)));
不过,弱类型 API 不能够返回正确的泛型表达式,因此生成一个编辑器来警告未检查的转换。一种消除这些烦人的警告消息的方法是使用 Java 泛型不常用的工具:参数化方法调用,比如 清单 19中通过调用 get()方法获取路径表达式。
可扩展数据库表达式
动态查询构造机制的独特优势是它的语法是可扩展的。例如,您可以在 QueryBuilder接口中使用 function()方法创建数据库支持的表达式:
Expression function(String name, Class type, Expression...args);
function()方法创建一个带有给定名称和 0 个或多个输入表达式的表达式。function()表达式的计算结果为给定的类型。这允许应用程序创建一个计算数据库的查询。例如,MySQL 数据库支持 CURRENT_USER()函数,它为服务器用于验证当前客户机的 MySQL 帐户返回一个由用户名和主机名组成的 UTF-8 字符串。应用程序可以在 CriteriaQuery中使用未带参数的 CURRENT_USER()函数,如清单 20 所示:
清单 20. 在 CriteriaQuery中使用特定于数据库的函数
CriteriaQuery q = cb.createTupleQuery();
Root c = q.from(Customer.class);
Expression currentUser =
cb.function("CURRENT_USER", String.class, (Expression[])null);
q.multiselect(currentUser, c.get(Customer_.balanceOwed));
注意,在 JPQL 中不能表达等效的查询,因为它的语法仅支持固定数量的表达式。动态 API 不受固定数量表达式的严格限制。
可编辑查询
可以以编程的方式编辑 CriteriaQuery。可以改变查询的子句,比如它的选择条件、WHERE子句中的选择谓词和 ORDER BY子句中的排序条件。可以在典型的 “在结果中搜索” 工具中使用这个编辑功能,以添加更多限制在后续步骤中进一步细化查询谓词。
清单 21 中的例子创建了一个根据名称对结果进行排序的查询,然后编辑该查询以根据邮政编码进行查询:
清单 21. 编辑 CriteriaQuery
CriteriaQuery c = cb.createQuery(Person.class);
Root p = c.from(Person.class);
c.orderBy(cb.asc(p.get(Person_.name)));
List result = em.createQuery(c).getResultList();
// start editing
List orders = c.getOrderList();
List newOrders = new ArrayList(orders);
newOrders.add(cb.desc(p.get(Person_.zipcode)));
c.orderBy(newOrders);
List result2 = em.createQuery(c).getResultList();
OpenJPA 中的常驻计算
通过使用 OpenJPA 的扩展特性,清单 21中的 “在结果中搜索” 例子可以在 内存中计算编辑查询,从而获得更高的效率。这个例子表明编辑查询的结果是原始结果的严格子集。因为 OpenJPA 能够在指定候选集合时从内存计算查询,所以仅需修改 清单 21的最后一行代码就可以提供原始查询的结果:
List result2 =
em.createQuery(c).setCandidateCollection(result).getResultList();
在 CriteriaQuery上的 setter 方法 — select()、where()或 orderBy() —使用新的参数替换先前的值。对应的 getter 方法(比如 getOrderList())返回的列表不是 活动的,即在返回列表上添加或删除元素不会导致修改 CriteriaQuery;另外,一些供应商甚至返回不可变的列表以阻止意外使用。因此,良好的实践是在添加和删除新的表达式之前,将返回列表复制到一个新的列表中。
根据例子进行查询
动态查询 API 中的另一个有用特性就是它能够轻松地支持 根据例子进行查询。根据例子进行查询(由 IBM?Research 在 1970 年开发出来)通常被作为早期的软件终端用户可用性例子引用。根据例子进行查询的理念使用模板实例,而不是为查询指定精确的谓词。有了给定的模板实例之后,将创建一个联合谓词,其中每个谓词都是模板实例的非 null 和非默认属性值。执行该查询将计算谓词以查找所有与模板实例匹配的实例。根据例子进行查询曾考虑添加到 JPA 2.0 中,但最终没有添加。OpenJPA 通过它的扩展 OpenJPAQueryBuilder接口支持这种查询,如清单 22 所示:
清单 22. 使用 OpenJPA 的 CriteriaQuery根据例子进行查询
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接口扩展支持以下表达式:
publicPredicateqbe(From from, T template);
这个表达式根据给定模板实例的属性值生成一个联合谓词。例如,这个查询将查询所有薪水为 10000评级为 1的 Employee。要进一步控制比较,可以指定不用于比较的可选属性,以及为值为 String的属性指定比较方式。(参考资料部分提供一个 Javadoc 链接,可以找到关于 OpenJPA 的 CriteriaQuery扩展的说明)。

回复

使用道具 举报

千问 | 2007-1-24 12:56:49 | 显示全部楼层
本文介绍了 JPA 2.0 中的新 Criteria API,它是一个用 Java 语言开发动态、类型安全的查询的机制。CriteriaQuery在运行时被构建为一个强类型查询表达式树,本文通过一系列例子展示了它的用法。
本文还确立了 Metamodel API 的关键角色,并展示了实例化元模型类如何使编译器能够检查查询的正确性,从而避免语法有误的 JPQL 查询引起的运行时错误。除了保证语法正确之外,JPA 2.0 以编程的方式构造查询的特性还能通过数据库函数实现更多强大的用途,比如通过例子进行查询。我希望本文的读者能够发现这些强大的新 API 的其他新用途。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

主题

0

回帖

4882万

积分

论坛元老

Rank: 8Rank: 8

积分
48824836
热门排行