JPA 20动态查询CriteriaWord文档下载推荐.docx
- 文档编号:18829633
- 上传时间:2023-01-01
- 格式:DOCX
- 页数:22
- 大小:79.92KB
JPA 20动态查询CriteriaWord文档下载推荐.docx
《JPA 20动态查询CriteriaWord文档下载推荐.docx》由会员分享,可在线阅读,更多相关《JPA 20动态查询CriteriaWord文档下载推荐.docx(22页珍藏版)》请在冰豆网上搜索。
∙EntityManager是构造一个包含给定JPQL字符串的可执行查询实例的工厂(第3行)。
∙查询执行的结果包含无类型的java.util.List的元素。
但是这个简单的例子有一个验证的错误。
该代码能够顺利通过编译,但将在运行时失败,因为该JPQL查询字符串的语法有误。
清单1的第2行的正确语法为:
selectpfromPersonpwherep.age>
不幸的是,Java编译器不能发现此类错误。
在运行时,该错误将出现在第3或第4行(具体行数取决于JPA提供者是否在查询构造或执行期间根据JPQL语法解析JPQL字符串)。
类型安全查询如何提供帮助?
CriteriaAPI的最大优势之一就是禁止构造语法错误的查询。
清单2使用CriteriaQuery接口重新编写了清单1中的JPQL查询:
清单2.编写CriteriaQuery的基本步骤
EntityManagerem=...
QueryBuilderqb=em.getQueryBuilder();
CriteriaQuery<
Person>
c=qb.createQuery(Person.class);
Root<
p=c.from(Person.class);
Predicatecondition=qb.gt(p.get(Person_.age),20);
c.where(condition);
TypedQuery<
q=em.createQuery(c);
List<
result=q.getResultList();
清单2展示了CriteriaAPI的核心构造及其基本使用:
∙第1行通过几种可用方法之一获取一个EntityManager实例。
∙在第2行,EntityManager创建QueryBuilder的一个实例。
QueryBuilder是CriteriaQuery的工厂。
∙在第3行,QueryBuilder工厂构造一个CriteriaQuery实例。
CriteriaQuery被赋予泛型类型。
泛型参数声明CriteriaQuery在执行时返回的结果的类型。
在构造CriteriaQuery时,您可以提供各种结果类型参数——从持久化实体(比如Person.class)到形式更加灵活的Object[]。
∙第4行在CriteriaQuery实例上设置了查询表达式。
查询表达式是在一个树中组装的核心单元或节点,用于指定CriteriaQuery。
图1显示了在CriteriaAPI中定义的查询表达式的层次结构:
图1.查询表达式中的接口层次结构
点击查看大图
首先,将CriteriaQuery设置为从Person.class查询。
结果返回Root<
实例p。
Root是一个查询表达式,它表示持久化实体的范围。
T>
实际上表示:
“对所有类型为T的实例计算这个查询。
”这类似于JPQL或SQL查询的FROM子句。
另外还需要注意,Root<
是泛型的(实际上每个表达式都是泛型的)。
类型参数就是表达式要计算的值的类型。
因此Root<
表示一个对Person.class进行计算的表达式。
第5行构造一个Predicate。
Predicate是计算结果为true或false的常见查询表达式形式。
谓词由QueryBuilder构造,QueryBuilder不仅是CriteriaQuery的工厂,同时也是查询表达式的工厂。
QueryBuilder包含构造传统JPQL语法支持的所有查询表达式的API方法,并且还包含额外的方法。
在清单2中,QueryBuilder用于构造一个表达式,它将计算第一个表达式参数的值是否大于第二个参数的值。
方法签名为:
∙
Predicategt(Expression<
?
extendsNumber>
x,Numbery);
这个方法签名是展示使用强类型语言(比如Java)定义能够检查正确性并阻止错误的API的好例子。
该方法签名指定,仅能将值为Number的表达式与另一个值也为Number的表达式进行比较(例如,不能与值为String的表达式进行比较):
第5行有更多学问。
注意qb.gt()方法的第一个输入参数:
p.get(Person_.age),其中p是先前获得的Root<
表达式。
p.get(Person_.age)是一个路径表达式。
路径表达式是通过一个或多个持久化属性从根表达式进行导航得到的结果。
因此,表达式p.get(Person_.age)表示使用Person的age属性从根表达式p导航。
您可能不明白Person_.age是什么。
您可以将其暂时看作一种表示Person的age属性的方法。
我将在谈论JPA2.0引入的新MetamodelAPI时详细解释Person_.age。
如前所述,每个查询表达式都是泛型的,以表示表达式计算的值的类型。
如果Person.class中的age属性被声明为类型Integer(或int),则表达式p.get(Person_.age)的计算结果的类型为Integer。
由于API中的类型安全继承,编辑器本身将对无意义的比较抛出错误,比如:
Predicatecondition=qb.gt(p.get(Person_.age,"
xyz"
));
∙第6行在CriteriaQuery上将谓词设置为其WHERE子句。
∙在第7行中,EntityManager创建一个可执行查询,其输入为CriteriaQuery。
这类似于构造一个输入为JPQL字符串的可执行查询。
但是由于输入CriteriaQuery包含更多的类型信息,所以得到的结果是TypedQuery,它是熟悉的javax.persistence.Query的一个扩展。
如其名所示,TypedQuery知道执行它返回的结果的类型。
它是这样定义的:
publicinterfaceTypedQuery<
extendsQuery{
List<
getResultList();
}
与对应的无类型超接口相反:
publicinterfaceQuery{
ListgetResultList();
很明显,TypedQuery结果具有相同的Person.class类型,该类型在构造输入CriteriaQuery时由QueryBuilder指定(第3行)。
∙在第8行中,当最终执行查询以获得结果列表时,携带的类型信息展示了其优势。
得到的结果是带有类型的Person列表,从而使开发人员在遍历生成的元素时省去麻烦的强制类型转换(同时减少了ClassCastException运行时错误)。
现在归纳清单2中的简单例子的基本方面:
∙CriteriaQuery是一个查询表达式节点树。
在传统的基于字符串的查询语言中,这些表达式节点用于指定查询子句,比如FROM、WHERE和ORDERBY。
图2显示了与查询相关的子句:
图2.CriteriaQuery封装了传统查询的子句
∙查询表达式被赋予泛型。
一些典型的表达式是:
oRoot<
,相当于一个FROM子句。
oPredicate,其计算为布尔值true或false(事实上,它被声明为interfacePredicateextendsExpression<
Boolean>
)。
oPath<
,表示从Root<
>
表达式导航到的持久化属性。
是一个没有父类的特殊Path<
。
∙QueryBuilder是CriteriaQuery和各种查询表达式的工厂。
∙CriteriaQuery被传递给一个可执行查询并保留类型信息,这样可以直接访问选择列表的元素,而不需要任何运行时强制类型转换。
持久化域的元模型
讨论清单2时指出了一个不常见的构造:
Person_.age,它表示Person的持久化属性age。
清单2使用Person_.age形成一个路径表达式,它通过p.get(Person_.age)从Root<
表达式p导航而来。
Person_.age是Person_类中的公共静态字段,Person_是静态、已实例化的规范元模型类,对应于原来的Person实体类。
元模型类描述持久化类的元数据。
如果一个类安装JPA2.0规范精确地描述持久化实体的元数据,那么该元模型类就是规范的。
规范的元模型类是静态的,因此它的所有成员变量都被声明为静态的(也是public的)。
Person_.age是静态成员变量之一。
您可以在开发时在源代码中生成一个具体的Person_.java来实例化一个规范类。
实例化之后,它就可以在编译期间以强类型的方式引用Person的持久化属性。
这个Person_metamodel类是引用Person的元信息的一种代替方法。
这种方法类似于经常使用(有人可能认为是滥用)的JavaReflectionAPI,但概念上有很大的不同。
您可以使用反射获得关于java.lang.Class的实例的元信息,但是不能以编译器能够检查的方式引用关于Person.class的元信息。
例如,使用反射时,您将这样引用Person.class中的age字段:
Fieldfield=Person.class.getField("
age"
);
不过,这种方法也存在很大的限制,类似于清单1中基于字符串的JPQL查询存在的限制。
编译器能够顺利编译该代码,但不能确定它是否可以正常工作。
如果该代码包含任何错误输入,它在运行时肯定会失败。
反射不能实现JPA2.0的类型安全查询API要实现的功能。
类型安全查询API必须让您的代码能够引用Person类中的持久化属性age,同时让编译器能够在编译期间检查错误。
JPA2.0提供的解决办法通过静态地公开相同的持久化属性实例化名为Person_的元模型类(对应于Person)。
关于元信息的讨论通常都是令人昏昏欲睡的。
所以我将为熟悉的PlainOldJavaObject(POJO)实体类展示一个具体的元模型类例子(domain.Person),如清单3所示:
清单3.一个简单的持久化实体
packagedomain;
@Entity
publicclassPerson{
@Id
privatelongssn;
privatestringname;
privateintage;
//publicgettter/settermethods
publicStringgetName(){...}
这是POJO的典型定义,并且包含注释(比如@Entity或@Id),从而让JPA提供者能够将这个类的实例作为持久化实体管理。
清单4显示了domain.Person的对应静态规范元模型类:
清单4.一个简单实体的规范元模型
importjavax.persistence.metamodel.SingularAttribute;
@javax.persistence.metamodel.StaticMetamodel(domain.Person.class)
publicclassPerson_{
publicstaticvolatileSingularAttribute<
Person,Long>
ssn;
Person,String>
name;
Person,Integer>
age;
元模型类将原来的domain.Person实体的每个持久化属性声明为类型为SingularAttribute<
Person,?
的静态公共字段。
通过利用这个Person_元模型类,可以在编译期间引用domain.Person的持久化属性age—不是通过ReflectionAPI,而是直接引用静态的Person_.age字段。
然后,编译器可以根据age属性声明的类型实施类型检查。
我已经列举了一个关于此类限制的例子:
QueryBuilder.gt(p.get(Person_.age),"
)将导致编译器错误,因为编译器通过QueryBuilder.gt(..)的签名和Person_.age的类型可以确定Person的age属性是一个数字字段,不能与String进行比较。
其他一些需要注意的要点包括:
∙元模型Person_.age字段被声明为类型javax.persistence.metamodel.SingularAttribute。
SingularAttribute是JPAMetamodelAPI中定义的接口之一,我将在下一小节描述它。
SingularAttribute<
Person,Integer>
的泛型参数表示该类声明原来的持久化属性和持久化属性本身的类型。
∙元模型类被注释为@StaticMetamodel(domain.Person.class)以将其标记为一个与原来的持久化domain.Person实体对应的元模型类。
MetamodelAPI
我将一个元模型类定义为一个持久化实体类的描述。
就像ReflectionAPI需要其他接口(比如java.lang.reflect.Field或java.lang.reflect.Method)来描述java.lang.Class的组成一样,JPAMetamodelAPI也需要其他接口(比如SingularAttribute和PluralAttribute)来描述元模型类的类型及其属性。
图3显示了在MetamodelAPI中定义用于描述类型的接口:
图3.MetamodelAPI中的持久化类型的接口的层次结构
图4显示了在MetamodelAPI中定义用于描述属性的接口:
图4.MetamodelAPI中的持久化属性的接口的层次结构
JPA的MetamodelAPI接口比JavaReflectionAPI更加专业化。
需要更细微的差别来表达关于持久化的丰富元信息。
例如,JavaReflectionAPI将所有Java类型表示为java.lang.Class。
即没有通过独立的定义对概念进行区分,比如类、抽象类和接口。
当然,您可以询问Class它是一个接口还是一个抽象类,但这与通过两个独立的定义表示接口和抽象类的差别不同。
JavaReflectionAPI在Java语言诞生时就被引入(对于一种常见的多用途编程语言而言,这曾经是一个非常前沿的概念),但是经过多年的发展才认识到强类型系统的用途和强大之处。
JPAMetamodelAPI将强类型引入到持久化实体中。
例如,持久化实体在语义上区分为MappedSuperClass、Entity和Embeddable。
在JPA2.0之前,这种语义区分是通过持久化类定义中的对应类级别注释来表示的。
JPAMetamodel在javax.persistence.metamodel包中描述了3个独立的接口(MappedSuperclassType、EntityType和EmbeddableType),以更加鲜明的对比它们的语义特征。
类似地,可以通过接口(比如SingularAttribute、CollectionAttribute和MapAttribute)在类型定义级别上区分持久化属性。
除了方便描述之外,这些专门化的元模型接口还有实用优势,能够帮助构建类型安全的查询从而减少运行时错误。
您在前面的例子中看到了一部分优势,随着我通过CriteriaQuery描述关于连接的例子,您将看到更多优势。
运行时作用域
一般而言,可以将JavaReflectionAPI的传统接口与专门用于描述持久化元数据的javax.persistence.metamodel的接口进行比较。
要进一步进行类比,则需要对元模型接口使用等效的运行时作用域概念。
java.lang.Class实例的作用域由java.lang.ClassLoader在运行时划分。
一组相互引用的Java类实例必须在ClassLoader作用域下定义。
作用域的边界是严格或封闭的,如果在ClassLoaderL作用域下定义的类A试图引用不在ClassLoaderL作用域之内的类B,结果将收到可怕的ClassNotFoundException或NoClassDefFoundError(对于处理包含多个ClassLoader的环境的开发人员或部署人员而言,问题就复杂了)。
现在将一组严格的可相互引用的类称为运行时作用域,而在JPA1.0中称为持久化单元。
持久化单元作用域的持久化实体在META-INF/persistence.xml文件的<
class>
子句中枚举。
在JPA2.0中,通过javax.persistence.metamodel.Metamodel接口让开发人员可以在运行时使用作用域。
Metamodel接口是特定持久化单元知道的所有持久化实体的容器,如图5所示:
图5.元模型接口是持久化单元中的类型的容器
这个接口允许通过元模型元素的对应持久化实体类访问元模型元素。
例如,要获得对Person持久化实体的持久化元数据的引用,可以编写:
EntityManagerFactoryemf=...;
Metamodelmetamodel=emf.getMetamodel();
EntityType<
pClass=metamodel.entity(Person.class);
这是一个用类的名称通过ClassLoader获得Class的类比:
ClassLoaderclassloader=Thread.currentThread().getContextClassLoader();
Class<
clazz=classloader.loadClass("
domain.Person"
可以在运行时浏览EntityType<
获得在Person实体中声明的持久化属性。
如果应用程序在pClass(比如pClass.getSingularAttribute("
Integer.class))上调用一个方法,它将返回一个SingularAttribute<
实例,该实例与实例化规范元模型类的静态Person_.age成员相同。
最重要的是,对于应用程序可以通过MetamodelAPI在运行时引用的属性,是通过实例化静态规范元模型Person_类向Java编译器提供的。
除了将持久化实体分解为对应的元模型元素之外,MetamodelAPI还允许访问所有已知的元模型类(Metamodel.getManagedTypes()),或者通过类的持久化信息访问元模型类,例如embeddable(Address.class),它将返回一个EmbeddableType<
Address>
实例(ManagedType<
>
的子接口)。
在JPA中,关于POJO的元信息使用带有源代码注释(或XML描述符)的持久化元信息进一步进行区分——比如类是否是嵌入的,或者哪个字段用作主键。
持久化元信息分为两大类:
持久化(比如@Entity)和映射(比如@Table)。
在JPA2.0中,元模型仅为持久化注释(不是映射注释)捕捉元数据。
因此,使用当前版本的MetamodelAPI可以知道哪些字段是持久化的,但不能找到它们映射到的数据库列。
规范和非规范
尽管JPA2.0规范规定了规范的静态元模型类的精确样式(包括元模型
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- JPA 20动态查询Criteria 20 动态 查询 Criteria