JPA查询语言(1)

这是www.sourcebeat.com上JPA101一书的样章,可以从TheServerSide.com上下载 ,作者blog是http://www.jroller.com/cmaki/entry/jpa_presentation,翻译问题请留言,修正之后会发布pdf版本。

摘要

本章探讨Java 持久化查询语言(JPQL ),在简单的向你介绍JPQL 后,会直接切入创建查询这个话题。本章会涉及一个查询的方方面面,包括fetch join 操作。fetch join 操作会提前读取延时关联(lazy relationship),以消除LazyInitializationException 异常,这在使用ORM 方案时,常常困扰很多应用程序的开发。接下来,会了解一下JPQL 的打量操作支持。学习完本章时,你会对JPQL 有一个全面的认识。

概述

JPQL 是一种与数据库无关的,基于实体(entity-based)的查询语言。它是EJB QL 的一个扩展,并加入了很多EJB QL 中所没有的新特性。JPQL 支持projection(可以查询某个实体的字段而不需要查询整个实体),批量操作(update和delete),子查询,join,group by和having操作。所有的JPQL 操作都在静态查询(命名查询,named query)和动态查询中得到支持。另外,JPQL 支持多态,当读取Rank 会返回SpecialRankPostCountRank 实例(instance)(Rank 是一个抽象类,JPQL 只返回具体类)。

JPQL 在语法上与SQL 相似:“ select from [where] [group by] [having] [order by] ”。如果你熟悉SQL ,那么对大部分的JPQL 语法也不会陌生。JPQL 同样支持SQL 类似的函数功能,如max和min,但是并不能取代SQL 。不管如何相似,但两者之间有一个重要的区别,JPQL 操作的是“抽象持久化模型(abstract persistence schema) ”,而不是数据库定义的物理模型。抽象持久化模型通常是用JPA 元数据(metadata)在ORM 文件中或注释(annotation)中进行定义(有关注释和ORM 的更多信息参见第2章Entities Part I,第3章Entities Part II,第4章Packaging)。抽象持久化模型包括实体,它们的字段及定义的所有关联关系。JPA 持久化工具可以将JPQL 转换成内置的SQL 查询语句。这就意味着你可以在查询语句的SELECT 指定一个实体或是它的字段,或者在WHERE 语句中使用多个实体。你可以用点号(.)来访问查询语句中的SELECT 部分的关联关系。下面这个例子是访问agoraBB 对象模型中从PrivateMessageUser 的关联。

 

				
SELECT p.toUser FROM PrivateMessage p

这个查询会得到PrivateMessage 所属的User

虽然可以说JPQL 可以将所有的操作当成查询,但实际上只有三种不同的“查询 ”:SELECT 查询(真正可以算得上的一个查询),更新查询(不是查询而是更新语句),或者删除查询(也不是真正的查询而是删除语句)。JPA 规范中将所有的这些查询当成查询,你可以用同样的语法定义这三种查询,它们之间的差别会反应到数据库上。

本章包含以下话题:

 

  • JPQL 概述

  • Join

  • WhereGroup ByHaving

  • SELECT 语句

  • 批量操作支持

  • 实例

JPQL 操作的就是抽象持久化模型,即一个持久化单元(persistence unit )中实体所表示的模型。使用SQL ,你可以直接查询数据库中表的字段;使用JPQL ,你可以查找实体的属性。一个JPQL 语句的各个组成部分都是针对实体或者实体的属性进行操作。你根本不会用到一个实体所映射的数据库表和字段。下面是你可以创建紧最基本的JPQL 语句:

 

SELECT u FROM User

查询从数据库返回所有的User 实例。

注意

除实体名称和实体字段外,JPQL 查询不区分大小写。所以在查询的其它部分你可以使用任意形式。本章中,所有JPA 保留标志符我们使用大写,在你的应用程序中并不要求要这么做。

类型

JPQL 是一种强类型语言,一个JPQL 语句中每个表达式都有类型。

注意

强类型语言 中规定某种表达式对某种类型是合法的。如果在Java 中编译"123"+3,你会得到一个类型不相容的编译器错误。

例如,有下面这样一个查询语句:

 

SELECT u.username FROM User u

在这个查询语句中,u.username 一个表达式的结果是一个String 类型。这个查询会返回一个String 对象的list。list中的各个对象代表系统中一个不同的用户名。JPQL 规范将一个实体(属性)中所饮食的各种类型称为抽象模型类型 (abstract schema type )。

一个实体可以包含一个或多个下面的抽象模型类型:

  • 状态字段(state-field):这种类型包括一个实体不代表关联关系的任何字段或属性。字段的类型或者一个属性get方法的返回结果决定了状态字段的抽象模型类型。

  • 关联字段(association-field):这种类型包括了一个实体中任何表示关联关系的字段或属性。关联字段的抽象模型类型就是目标实体的抽象模型类型。

JPQL 语句的作用范围是由你执行查询语句中持久化单元(persistence unit)的实体决定的。

在一个查询中,实体是通过实体名称来引用的。实体的名称就是你使用@Entity 注释时指定的名称,或者是实体的限定词(unqualified name)。假如有这样一个实体。

 

			
package com.sourcebeat.jpa.model;

@Entity(name="message")
public class PrivateMessage extends ModelBase {
// fields/methods removed for readability
}

实体名称是“ message ”,因为你在使用@Entity 时候指定了名称(在一个持久化单元内实体名称必须是唯一的)。在下面的代码中,实体的限定词是User (以一个大写的“U ”开头)。实体的全名是com.sourcebeat.jpa.model.User ,但它的限定词 是User

 

				
package com.sourcebeat.jpa.model;

@Entity
public class User extends ModelBase {
// fields/methods removed for readability
}

当我们在JPQL 中引用实体时,必须使用@Entity 注释指定的名称或是实体的限定词 。一些JPA 实现(如,Hibernate )允许你使用全名,但有的(如,Toplink )则抛出异常。为了保证移植性,在查询中最好使用限定词 。

一个标识变量(identification variable)是一个在FROM 语句中指定的一个标志符号。在下面的查询语句中,u 就是一个标识变量。u 类型就是能够识别名称User 的实体的抽象模型类型。在本例中,实体是User (前面已经定义),所以u 的类型是User

 

SELECT u FROM User u				

标识变量也可以使用JOIN 关键字定义,例如。

 

SELECT r.name FROM User u JOIN u.roles r				

这里,标识变量有urr 表示任何能够从一个User 实例中直接访问的Role

保留标志符

JPA 定义了以下保留标志符(虽然下面列表中显示是大写形式,保留标志符并不区分大小写,所以SELECTselectSelECt 等同。)

 

SELECT, FROM, WHERE, UPDATE, DELETE, JOIN, OUTER, INNER, LEFT, GROUP, 
BY, HAVING, FETCH, DISTINCT, OBJECT, NULL, TRUE, FALSE, NOT, AND, OR,
BETWEEN, LIKE, IN, AS, UNKNOWN, EMPTY, MEMBER, OF, IS, AVG, MAX, MIN,
SUM, COUNT, ORDER, BY, ASC, DESC, MOD, UPPER, LOWER, TRIM, POSITION,
CHARACTER_LENGTH, CHAR_LENGTH, BIT_LENGTH, CURRENT_TIME, CURRENT_DATE,
CURRENT_TIMESTAMP, NEW, EXISTS, ALL, ANY, SOME.

UNKNOWN 目前在JPQL 中还没有用到。

路径表达式

路径表达式就是一个标识符号紧跟一个访问操作符(.)再紧跟一个状态字段或是关联字段。只要它们不是集合(collections),就可以遍历关系。下图是agoraBB 项目对象模型的一部分,用于解释路径表达式。

图 1. 路径表达式模型实例

路径表达式模型实例

PrivateMessage 所属于的User 的关系是由PrivateMessage 的关联关系字段toUser 表示的。User 有一个关联到Role 的集合关联字段roles ,还有一个关联到UserIpAddress 的字段userIpAddress

由于在在这些关系,你可以进行以下操作。

  • PrivateMessage 所属用户User 的IP地址UserIpAddresses

    SELECT p.toUser.userIPAddresses from PrivateMessage p
  • PrivateMessage 所属用户User 的角色Role

    SELECT p.toUser.roles from PrivateMessage p
  • User 的角色Role ,获取所有的无重复的角色名称。

    SELECT DISTINCT r.name FROM User u JOIN u.roles r
  • User 的IP地址UserIpAddresses

    SELECT DISTINCT u.userIPAddresses FROM User u

下图关系略有不同。

图 2. 路径表达式模型实例2

路径表达式模型实例2

图中,论坛Forum 与其主题Topics 的关系由Forum 的字段topics 表示,从TopicsPost 的关系由Topic 的字段topics 表示。从对象关系图可以看出哪些JPQL 是合法的,哪些不合法。

  • 查询论坛Forum 的主题Topic ,要获取标题(subject):路径表达是不合法的,不能直接通过集合访问。

    SELECT f.topics.subject FROM Forum f – ILLEGAL
  • 查询论坛Forum 主题Topic 的帖子Post ,使用JOIN 操作符,表达式是合法的。

    SELECT t.subject FROM Forum f JOIN f.topics AS t
  • 查询论坛Forum 主题Topic 的帖子Post ,,路径表达是不合法的,不能直接通过集合访问。

    SELECT f.topics.posts FROM Forum f – ILLEGAL
  • 查询论坛Forum 主题Topic 的帖子Post ,使用JOIN 操作符,表达式是合法的。

    SELECT p FROM Forum f JOIN f.topics t JOIN t.posts p

注意

虽然JPA 规范中明确表示无法访问一个集合关系字段,据我在HibernateToplink 上的测试,在一个目标实体中可以访问集合关联字段或状态字段。在JPA 的BNF范式针对路径访问指明了从Forum 访问Topic ,所以读取主题就是非法的。如果你要访问一个集合字段的目标,使用第二个例子如示JOIN 语法。

总之,你可以使用导航操作符(.)来遍历实体对象关系图,查询的类型是由SELECT 语句决定的。SELECT 语句可以包含标识变量和路径表达式。路径表达式可以遍历整个对象关系图,只要你从左到右访问的是单值关联字段,你无法访问一个集合字段或是一个状态关联字段。

界定变量(Range Variables)

界定变量使用与&SQL 相似的语法,将实体名绑定到一个标志符上。界定变量声明如下(在一个查询语句的SELECT 语句):

 

entityName [AS] identification_variable				

你可以通过使用多个界定变量来使用同一实体,下面的例子来自JPA 规范。

 

SELECT DISTINCT o1
FROM Order o1, Order o2
WHERE o1.quantity > o2.quantity AND
o2.customer.lastname = ‘Smith’ AND
o2.customer.firstname= ‘John’

此查询返回大于John Smith的定单的所有的订单。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值