查询方法
您通常在 repository 上触发的大多数数据访问操作都会导致针对数据库运行查询。定义此类查询的方法是在 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
结合使用,以创建删除匹配行的派生查询。
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 Repositories 的自定义实现中描述的功能来添加自定义修改行为。
使用 @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);
}
请注意,基于 String 的查询不支持分页,也不接受 Sort 、PageRequest 和 Limit 作为查询参数,因为对于这些查询,需要重写查询。如果您想应用限制,请使用 SQL 表达此意图并自行将适当的参数绑定到查询。 |
Spring 完全支持基于 -parameters 编译器标志的 Java 8 参数名称发现。在您的构建中使用此标志作为调试信息的替代方案,可以省略命名参数的 @Param 注解。 |
包含 SpEL 表达式的查询
查询字符串定义可以与 SpEL 表达式一起使用,以在运行时创建动态查询。SpEL 表达式可以通过两种方式使用。
SpEL 表达式可以提供断言值 (predicate values),这些值在运行查询之前进行评估。
表达式通过一个包含所有参数的数组暴露方法参数。以下查询使用 [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);
它在第一次执行前评估一次,并使用一个添加了 tableName
和 qualifiedTableName
两个变量的 StandardEvaluationContext
。当表名本身是动态的(因为它们也使用 SpEL 表达式)时,这种用法最有用。
在查询字符串中使用 SpEL 是增强查询的强大方法。但是,它们也可能接受各种不必要的参数。在将字符串传递给查询之前,您应该确保对其进行净化(sanitize),以避免对查询造成不必要的更改。