排序(ORDER BY)
ORDER BY
允许你对查询结果进行排序。数据库排序比应用程序更有效。要依据集合的顺序,可以使用
ORDER BY
操作符。(更多信息参见第三章Entities Part II中
@OrderBy
注释)
这是一个JPQL查询语句中
Order By
操作符的所在位置。
select from [where] [group by] [having] [order by]
Order By
的语法为:
ORDER BY expression [ASC | DESC] {, expression [ASC | DESC]}*
下面是一些合法的
Order By
实例。
-
SELECT u FROM User u ORDER BY u
-
SELECT u FROM User u ORDER BY u.address
-
SELECT u.username, u.id FROM User u ORDER BY u.username DESC
expression
可以是一个标识符号变量,一个单值关联路径,一个状态字段路径表达式。这些查询语句一一演示了
Order By
的这些类型。
你必须注意使用
Order By
操作符的一些限制。
-
当使用一个标识符号变量,一个单值关联字段时,你用来对查询进行排序的项目必须是一个可排序的类型,例如数字类型,字符串,字符类型,或是时间日期。
-
如果你使用一个状态字段路径表达式,同时它也必须出现在
SELECT
语句中。
ASC
表示升序(由小到大),是默认的排序方式。
DESC
表示降序排列(由大到小),使用时显式在查询的
Order By
语句上添加它。排序的优先级是按罗列的字段从左到右的顺序进行。
不难理解,下面的查询是不合法的,因为它对一个字段进行排序但它却不在
SELECT
语句中。
SELECT u.username, u.id FROM User u ORDER BY u.password
JPQL提供了一种替代方案,用一条语句更新或删除一个或多个实体。JPQL中批处理支持一次可以处理一个实体类型(和它的子类)。也就是说,你只能在
FROM
或
UPDATE
指定唯一实体。这里是一个
UPDATE
查询的语法。
UPDATE abstract_persistence_scheama_name [AS] identification_variable
SET state_field | single_value_association_field = value
{,state_field | single_value_association_field = value }*
value
引用必须与你要更新的状态字段或是单值关联字段的类型相同。你可以将下列任意值用于
value
。
-
一个算术表达式
-
字符串
-
时间日期(datetime)
-
布尔值(boolean)
-
枚举类型(enum)
-
简单实体表达式
-
NULL
DELETE
语句看起来如下。
DELETE FROM abstract_persistence_scheama_name [[AS] identification_variable]
[WHERE clause]
WHERE
语句的语法与
SELECT
相同(更多信息参见
SELECT
语句一节)。
DELETE
操作符只影响
FROM
语句指定的实体及其子类。此操作不会级联到任何相关的实体。此外,
UPDATE
操作符不更新实体的version一列。
批处理操作最终转换成SQL并在数据库中执行,绕过了持久化环境(persistence context)。当使用一个事务作用域(transaction-scoped)的持久化环境,会在它们所在事务内执行或在一个事务开始时执行。
批量操作和扩展的持久化环境(extended persistence context)的组合在管理上有所不同。因为一个扩展持久化环境不会与数据库同步,直到参与到一个事务中去,你可以有一些实体已经删除了,但仍可能存在于持久化环境中。
持久化实现通常会在执行批处理操作前禁用一些缓存功能。基于不同的实现,部分或全部缓存功能会被禁用。频繁的使用批处理操作会影响应用程序的性能。基于这些原因,你应该在它们自己的事务内或是一个事务开始时执行批处理操作。
当你使用一个
UPDATE
或是
DELETE
查询时,你一定会用到Query API提供的方法--
executeUpdate()
,来执行更新或是删除操作。如果你使用
getResultList()
或
getSingleResult()
,持久化实现会抛出一个
IllegalStateException
异常。同样,你用
executeUpdate()
来执行一个
SELECT
查询,持久化实现会抛出一个
IllegalStateException
异常。
下面是一些批量更新的例子。
-
一个论坛帖子数量的两倍。
Query q2 = em.createQuery("UPDATE Forum AS f "
+"SET f.forumPostCount = f.forumPostCount * 2");
q2.executeUpdate();
-
将所有的
Role
实体的
dateUpdated
字段设置为当前日期和时间。
Query q = em.createQuery("UPDATE Role AS r " +
"SET r.dateUpdated = CURRENT_TIMESTAMP");
q.executeUpdate();
-
将布尔字(
pruningEnabled)
)段值设置为
true
。在
agoraBB
应用程序中,用
EntityListener
类来设置
dateUpdated
和
updatedByUser
字段。version字段是由持久化实现进行管理。避开这些弯弯角角,当你执行批量操作时,你的查询写成更新字段。还有,要注意的是,
executeUpdate
返回实体更新的数目(删除时返回删除的数量)。
// Assume we already fetched the correct User identified by
// the variable adminUser
Query forumUpdate = em.createQuery("UPDATE Forum AS f " +
"SET f.pruningEnabled = TRUE, f.dateUpdated = CURRENT_TIMESTAMP, " +
"f.version = f.version + 1, f.updatedByUser = :user");
forumUpdate.setParameter("user", adminUser);
int entitiesUpdated = forumUpdate.executeUpdate();
你可以将
pruningEnabled
的值设置为
null
,将其进行重位。如下所示。
Query pruningReset = em.createQuery("UPDATE Forum AS f " +
"SET f.pruningEnabled = NULL");
pruningReset.executeUpdate();
这条查询设置了
enum
类型的字段值。你必须使用
enum
类的长限定词,因为
enum
不是实体,持久化实现不法获得Status的信息,但它可以识别
com.sourcebeat.jpa.model.Status
。
Query enumUpdate = em.createQuery("UPDATE Forum AS f " +
"SET f.status = com.sourcebeat.jpa.model.Status.LOCKED " +
"WHERE f.type = com.sourcebeat.jpa.model.FTPType.ANNONUCEMENT");
int enumUpdateCount = enumUpdate.executeUpdate();
这里有几个
DELETE
操作的例子。
-
删除系统中所有无密码的用户(
Users
)。
Query removeRoles = em.createQuery("DELETE FROM User u " +
"WHERE u.password = NULL");
int rolesRemoved = removeRoles.executeUpdate();
-
删除没有主题(
Topics
)的论坛(
Forum
)。
Query removeForums = em.createQuery("DELETE FROM Forum f " +
"WHERE f.topics IS EMPTY");
int forumsRemoved = removeForums.executeUpdate();
JPQL提供了大量实体查询,分组,排序和总结的功能。包括丰富的关联(join)操作支持和即时读取延迟关联关系(eagerly fetch lazy relationship)的能力。你已经学习所支持的各种函数和表达式,以及
SELECT
语句所能提供的各种选项。本章最后探讨了批量处理操作,它具备使用单条查询语句(实际是指
UPDATE
或
DELETE
语句)影响一个或多个实体的能力。
现在你应该很熟悉了JPQL所能提供的丰富的功能,利用它,从简单到复杂一步步地创建自己的查询。