|
对于最新的稳定版本,请使用 Spring Data Relational 4.0.4! |
查询方法
通常,你在仓库(repository)上触发的大多数数据访问操作都会导致在数据库中执行查询。 定义这样的查询只需在仓库接口中声明一个方法即可,如下例所示:
interface ReactivePersonRepository extends ReactiveSortingRepository<Person, Long> {
Flux<Person> findByFirstname(String firstname); (1)
Flux<Person> findByFirstname(Publisher<String> firstname); (2)
Flux<Person> findByFirstnameOrderByLastname(String firstname, Pageable pageable); (3)
Mono<Person> findByFirstnameAndLastname(String firstname, String lastname); (4)
Mono<Person> findFirstByLastname(String lastname); (5)
@Query("SELECT * FROM person WHERE lastname = :lastname")
Flux<Person> findByLastname(String lastname); (6)
@Query("SELECT firstname, lastname FROM person WHERE lastname = $1")
Mono<Person> findFirstByLastname(String lastname); (7)
}
| 1 | 该方法展示了查询所有具有指定 firstname 的人员。
查询语句是通过解析方法名称中的约束条件生成的,这些约束条件可以用 And 和 Or 连接起来。
因此,该方法名会生成一个查询表达式:SELECT … FROM person WHERE firstname = :firstname。 |
| 2 | 该方法展示了一个查询,用于查找所有具有指定 firstname 的人员,前提是该 firstname 由给定的 Publisher 发出。 |
| 3 | 使用 Pageable 将偏移量和排序参数传递给数据库。 |
| 4 | 根据给定条件查找单个实体。
当结果不唯一时,会抛出 IncorrectResultSizeDataAccessException 异常。 |
| 5 | 除非 <4>,否则查询返回更多结果行时第一个实体始终会被发出。 |
| 6 | findByLastname 方法展示了查询所有具有指定姓氏的人员的查询语句。 |
| 7 | 一个查询单个 Person 实体的查询,仅投影 firstname 和 lastname 列。
该注解查询使用了原生绑定标记符,本例中为 Postgres 的绑定标记符。 |
请注意,在 @Query 注解中使用的 SELECT 语句的列名必须与相应属性由 NamingStrategy 生成的名称相匹配。
如果 SELECT 语句中未包含匹配的列,则该属性不会被设置。
如果该属性是持久化构造函数所必需的,则会提供 null 值(对于基本类型则提供默认值)。
下表列出了查询方法所支持的关键字:
| 关键字 | 示例 | 逻辑结果 |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
修改查询
前面的章节描述了如何声明查询以访问给定的实体或实体集合。
可以结合使用上表中的关键字与 delete…By 或 remove…By,创建用于删除匹配行的派生查询。
Delete…By 查询interface ReactivePersonRepository extends ReactiveSortingRepository<Person, String> {
Mono<Integer> deleteByLastname(String lastname); (1)
Mono<Void> deletePersonByLastname(String lastname); (2)
Mono<Boolean> deletePersonByLastname(String lastname); (3)
}
| 1 | 使用返回类型 Mono<Integer> 将返回受影响的行数。 |
| 2 | 使用 Void 仅用于报告行是否已成功删除,而不会发出结果值。 |
| 3 | 使用 Boolean 报告是否至少删除了一行。 |
由于这种方法适用于全面的自定义功能,因此对于仅需参数绑定的查询,您可以通过在查询方法上添加 @Modifying 注解来进行修改,如下例所示:
@Modifying
@Query("UPDATE person SET firstname = :firstname where lastname = :lastname")
Mono<Integer> setFixedFirstnameFor(String firstname, String lastname);
修改查询的结果可以是:
-
Void(或 Kotlin 中的Unit)用于忽略更新计数并等待操作完成。 -
Integer或其他发出受影响行数的数值类型。 -
Boolean类型,用于指示是否至少有一行被更新。
@Modifying 注解仅在与 @Query 注解结合使用时才有意义。
派生的自定义方法不需要此注解。
修改型查询直接在数据库上执行。 不会触发任何事件或回调。 因此,如果带审计注解的字段未在注解查询中显式更新,这些字段也不会被更新。
或者,您可以使用Spring Data 仓库的自定义实现中描述的功能来添加自定义的修改行为。
使用@Query
以下示例展示了如何使用 @Query 来声明一个查询方法:
interface UserRepository extends ReactiveCrudRepository<User, Long> {
@Query("select firstName, lastName from User u where u.emailAddress = :email")
Flux<User> findByEmailAddress(@Param("email") String email);
}
请注意,基于字符串的查询不支持分页,也不接受 Sort、PageRequest 和 Limit 作为查询参数,因为对于这些查询,需要重写查询语句。
如果您希望应用限制,请使用 SQL 明确表达此意图,并自行将相应的参数绑定到查询中。 |
Spring 完全支持基于 -parameters 编译器标志的 Java 8 参数名发现功能。
在构建过程中使用此标志作为调试信息的替代方案,您可以省略命名参数上的 @Param 注解。 |
使用 SpEL 表达式的查询
查询字符串定义可以与 SpEL 表达式结合使用,以在运行时创建动态查询。 SpEL 表达式有两种使用方式。
SpEL 表达式可以提供谓词值,这些值会在运行查询之前进行求值。
表达式通过一个包含所有参数的数组来暴露方法参数。
以下查询使用 [0]
来声明 lastname 的谓词值(这等同于 :lastname 参数绑定):
@Query("SELECT * FROM person WHERE lastname = :#{[0]}")
Flux<Person> findByQueryWithParameterExpression(String lastname);
此表达式支持可通过查询 SPI 进行扩展:org.springframework.data.spel.spi.EvaluationContextExtension。
该查询 SPI 可以添加属性和函数,并可自定义根对象。
在构建查询时进行 SpEL 表达式求值的过程中,扩展会从应用上下文中获取。
| 当将 SpEL 表达式与普通参数结合使用时,请使用命名参数符号,而不是原生的绑定标记,以确保正确的绑定顺序。 |
在查询语句中间使用表达式的另一种方式,与参数无关。 对表达式求值的结果将替换查询字符串中的该表达式。
@Query("SELECT * FROM #{#tableName} WHERE lastname = :lastname")
Flux<Person> findByQueryWithExpression(String lastname);
它在首次执行前被评估一次,并使用一个 StandardEvaluationContext,其中添加了两个变量 tableName 和 qualifiedTableName。
这种用法在表名本身是动态的情况下最为有用,因为它们也使用了 SpEL 表达式。
查询字符串中的 SpEL 是一种增强查询功能的强大方式。 然而,它们也可能接受大量不希望传入的参数。 在将字符串传递给查询之前,应确保对其进行清理,以避免对查询造成意外的修改。