JPA查询语言(1)

摘要

本章探讨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

会返回

SpecialRank

PostCountRank

实例(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

对象模型中从

PrivateMessage

User

的关联。

SELECT p.toUser FROM PrivateMessage p

这个查询会得到

PrivateMessage

所属的

User

虽然可以说JPQL可以将所有的操作当成查询,但实际上只有三种不同的“查询”:

SELECT

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

本章包含以下话题:

  • JPQL概述

  • Join

  • Where

    Group By

    Having

  • 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

这里,标识变量有

u

r

r

表示任何能够从一个

User

实例中直接访问的

Role

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

SELECT

select

SelECt

等同。)

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

图中,论坛

Forum

与其主题

Topics

的关系由

Forum

的字段

topics

表示,从

Topics

Post

的关系由

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规范中明确表示无法访问一个集合关系字段,据我在Hibernate和Toplink上的测试,在一个目标实体中可以访问集合关联字段或状态字段。在JPA的BNF范式针对路径访问指明了从

Forum

访问

Topic

,所以读取主题就是非法的。如果你要访问一个集合字段的目标,使用第二个例子如示

JOIN

语法。

总之,你可以使用导航操作符(.)来遍历实体对象关系图,查询的类型是由

SELECT

语句决定的。

SELECT

语句可以包含标识变量和路径表达式。路径表达式可以遍历整个对象关系图,只要你从左到右访问的是单值关联字段,你无法访问一个集合字段或是一个状态关联字段。

界定变量使用与&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的定单的所有的订单

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值