查询方法

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

示例 1. 带有查询方法的人员仓库
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 该方法显示了一个查询,用于在给定Publisher发出firstname后,获取所有具有给定firstname的人员。
3 使用 Pageable 将偏移量和排序参数传递给数据库。
4 根据给定条件查找单个实体。如果结果不唯一,则以 IncorrectResultSizeDataAccessException 完成。
5 除非 <4>,即使查询产生多行结果,也始终会发出第一个实体。
6 findByLastname方法显示了一个查询,用于获取所有具有给定姓氏的人员。
7 这是一个针对单个Person实体的查询,仅投影firstnamelastname列。带注解的查询使用原生绑定标记,在此示例中是 Postgres 绑定标记。

请注意,@Query注解中使用的 select 语句的列必须与NamingStrategy为相应属性生成的名称匹配。如果 select 语句不包含匹配的列,则该属性不会被设置。如果持久化构造函数需要该属性,则会提供 null 或(对于原始类型)默认值。

下表显示了查询方法支持的关键字

表 1. 支持的查询方法关键字
关键字 示例 逻辑结果

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)

年龄 <= 年龄

Between

findByAgeBetween(int from, int to)

年龄 介于 起始年龄 和 结束年龄 之间

不介于

findByAgeNotBetween(int from, int to)

年龄 不介于 起始年龄 和 结束年龄 之间

In

findByAgeIn(Collection<Integer> ages)

年龄 在 (年龄1, 年龄2, 年龄N) 中

NotIn

findByAgeNotIn(Collection ages)

年龄 不在 (年龄1, 年龄2, 年龄N) 中

IsNotNull, NotNull

findByFirstnameNotNull()

名 不为空

IsNull, Null

findByFirstnameNull()

名 为空

Like, StartingWith, EndingWith

findByFirstnameLike(String name)

名 像 姓名

NotLike, IsNotLike

findByFirstnameNotLike(String name)

名 不像 姓名

字符串上的 Containing

findByFirstnameContaining(String name)

名 像 '%' + 姓名 +'%'

NotContaining 用于 String

findByFirstnameNotContaining(String name)

名 不像 '%' + 姓名 +'%'

(无关键字)

findByFirstname(String name)

firstname = name

Not

findByFirstnameNot(String name)

名 != 姓名

IsTrue, True

findByActiveIsTrue()

激活 为真

IsFalse, False

findByActiveIsFalse()

激活 为假

修改查询

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

示例 2. 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声明查询方法

使用 @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注解。

带 SpEL 表达式的查询

查询字符串定义可以与 SpEL 表达式一起使用,以在运行时创建动态查询。SpEL 表达式可以通过两种方式使用。

SpEL 表达式可以提供谓词值,这些值在运行查询之前进行评估。

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

@Query("SELECT * FROM person WHERE lastname = :#{[0]}")
Flux<Person> findByQueryWithParameterExpression(String lastname);

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

当 SpEL 表达式与纯参数结合使用时,请使用命名参数表示法而不是原生绑定标记,以确保正确的绑定顺序。

另一种使用表达式的方式是在查询中间,独立于参数。评估查询的结果将替换查询字符串中的表达式。

在查询中使用 SpEL
@Query("SELECT * FROM #{#tableName} WHERE lastname = :lastname")
Flux<Person> findByQueryWithExpression(String lastname);

它在首次执行前评估一次,并使用一个添加了tableNamequalifiedTableName两个变量的StandardEvaluationContext。当表名本身是动态的,因为它们也使用 SpEL 表达式时,这种用法最有用。

查询字符串中的 SpEL 可以是增强查询的强大方式。然而,它们也可以接受广泛的不需要的参数。您应该确保在将字符串传递给查询之前对其进行清理,以避免对查询造成不必要的更改。

© . This site is unofficial and not affiliated with VMware.