Query Methods

大多数您通常在存储库上触发的数据库访问操作会导致针对数据库运行查询。定义此类查询是声明存储库接口方法的问题,如下例所示: .PersonRepository with query methods

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 的所有人的查询。该查询是通过解析方法名称来获取可与 AndOr 连接的约束而生成的。因此,方法名称会生成 SELECT … FROM person WHERE firstname = :firstname 的查询表达式。
2 该方法显示了对具有给定 `firstname`的所有人的查询,只要 `firstname`由给定的 `Publisher`发出即可。
3 使用 Pageable 将偏移量和排序参数传递给数据库。
4 查找给定条件的单个实体。在非唯一结果上用 IncorrectResultSizeDataAccessException 完成。
5 除非 <4>,否则即使查询产生更多结果行,也总是会发出第一个实体。
6 findByLastname 方法显示了对具有给定姓氏的所有人的查询。
7 对单个 Person 实体查询只投影 firstnamelastname 列。带注释的查询使用本机绑定标记,在本示例中为 Postgres 绑定标记。

请注意,在 @Query 注释中使用的选择语句的列必须与 NamingStrategy 为相应属性生成的名称匹配。如果选择语句不包含匹配的列,则不设置该属性。如果持久化构造函数需要该属性,则提供空值或(对于基本类型)默认值。 下表显示了查询方法支持的关键字:

Table 1. Supported keywords for query methods
Keyword Sample Logical result

After

findByBirthdateAfter(Date date)

birthdate > date

GreaterThan

findByAgeGreaterThan(int age)

age > age

GreaterThanEqual

findByAgeGreaterThanEqual(int age)

age >= age

Before

findByBirthdateBefore(Date date)

birthdate < date

LessThan

findByAgeLessThan(int age)

age < age

LessThanEqual

findByAgeLessThanEqual(int age)

age ⇐ age

Between

findByAgeBetween(int from, int to)

age BETWEEN from AND to

NotBetween

findByAgeNotBetween(int from, int to)

age NOT BETWEEN from AND to

In

findByAgeIn(Collection<Integer> ages)

age IN (age1, age2, ageN)

NotIn

findByAgeNotIn(Collection ages)

age NOT IN (age1, age2, ageN)

IsNotNull, NotNull

findByFirstnameNotNull()

firstname IS NOT NULL

IsNull, Null

findByFirstnameNull()

firstname IS NULL

Like, StartingWith, EndingWith

findByFirstnameLike(String name)

firstname LIKE name

NotLike, IsNotLike

findByFirstnameNotLike(String name)

firstname NOT LIKE name

Containing on String

findByFirstnameContaining(String name)

firstname LIKE '%' + name +'%'

NotContaining on String

findByFirstnameNotContaining(String name)

firstname NOT LIKE '%' + name +'%'

(No keyword)

findByFirstname(String name)

firstname = name

Not

findByFirstnameNot(String name)

firstname != name

IsTrue, True

findByActiveIsTrue()

active IS TRUE

IsFalse, False

findByActiveIsFalse()

active IS FALSE

Modifying Queries

前面的章节描述了如何声明查询来访问给定的实体或实体集合。可以将前一个表中的关键字与 delete…Byremove…By 结合使用,以创建派生的查询来删除匹配的行。

Example 1. Delete…By Query
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 注释查询方法,如下例所示:

link:example$r2dbc/PersonRepository.java[role=include]

修改查询的结果可以是:

  • Void (或 Kotlin Unit)以放弃更新计数并等待完成。

  • Integer 或发出受影响的行数的另一数值类型。

  • Boolean 以发出是否更新了至少一行。

@Modifying 注释仅与 @Query 注释相结合时才相关。派生的自定义方法不需要此注释。

修改查询直接针对数据库执行。不会调用事件或回调。因此,如果审计注释中的字段不在带注释的查询中更新,则它们也不会更新。

或者,你可以使用 Custom Implementations for Spring Data Repositories中描述的功能添加自定义修改行为。

Using @Query

以下示例展示如何使用 @Query 声明查询方法:

Declare a query method by using @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);
}

请注意,基于字符串的查询不支持分页,也不接受 SortPageRequestLimit 作为查询参数,因为对于这些查询,需要重写查询。如果您想应用限制,请使用 SQL 表达这种意图,并自己将适当的参数绑定到查询。

Spring 完全支持基于 -parameters 编译器标志的 Java 8 的参数名称发现。在您的构建中将这个标志作为调试信息的替代,您可以省略带命名参数的 @Param 注释。

Queries with SpEL Expressions

查询字符串定义可与 SpEL 表达式一起使用,以在运行时创建动态查询。SpEL 表达式可以提供谓词值,这些值在运行查询正文之前进行评估。

表达式通过包含所有参数的数组公开方法参数。以下查询使用 `[0]`声明 `lastname`的谓词值(这等同于 `:lastname`参数绑定):

link:example$r2dbc/PersonRepository.java[role=include]

查询字符串中的 SpEL 可以增强查询功能。但是,它们也可以接受各种不受欢迎的参数。在将字符串传递到查询之前,您应该确保对其进行清理,以避免对查询进行不必要的更改。

表达式支持可以通过查询 SPI 扩展:org.springframework.data.spel.spi.EvaluationContextExtension。查询 SPI 可以提供属性和函数,并且可以自定义根对象。在构建查询时,会在 SpEL 评估时从应用程序上下文中检索扩展。

在将 SpEL 表达式与普通参数结合使用时,请使用命名参数表示法,而不是本地绑定标记,以确保正确的绑定顺序。