查询方法
标准CRUD功能存储库通常对基础数据存储区进行查询。使用Spring Data,声明这些查询将分为四个步骤:
-
声明扩展Repository或其子接口之一的接口,并将其键入它应处理的域类和ID类型,如以下示例所示:
interface PersonRepository extends Repository<Person, Long> { … }
-
在接口上声明查询方法。
interface PersonRepository extends Repository<Person, Long> {List<Person> findByLastname(String lastname); }
-
设置Spring以使用JavaConfig或XML配置为这些接口创建代理实例。
-
要使用Java配置,请创建类似于以下内容的类:
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;@EnableJpaRepositories class Config {}
-
要使用XML配置,请定义类似于以下内容的bean:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:jpa="http://www.springframework.org/schema/data/jpa"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/data/jpahttp://www.springframework.org/schema/data/jpa/spring-jpa.xsd"><jpa:repositories base-package="com.acme.repositories"/></beans>
在此示例中使用JPA名称空间。如果对任何其他商店使用存储库抽象,则需要将其更改为商店模块的相应名称空间声明。换句话说,你应该换一个
jpa
赞成,例如,mongodb
。+另请注意,JavaConfig变体未显式配置包,因为默认情况下使用带注释的类的包。要自定义要扫描的包,请使用
basePackage…
特定于数据存储库的@Enable${store}Repositories
-annotation 的属性之一。 -
-
注入存储库实例并使用它,如以下示例所示:
class SomeClient {private final PersonRepository repository;SomeClient(PersonRepository repository) {this.repository = repository;}void doSomething() {List<Person> persons = repository.findByLastname("Matthews");} }
以下各节详细说明了每个步骤:
-
定义存储库接口
-
定义查询方法
-
创建存储库实例
-
Spring Data Repositories的自定义实现
定义查询方法
存储库代理有两种方法可以从方法名称派生特定于商店的查询:
-
通过直接从方法名称派生查询。
-
通过使用手动定义的查询。
-
可用选项取决于实际商店。但是,必须有一个策略来决定创建实际查询的内容。下一节将介绍可用选项。
4.4.1。查询查找策略
存储库基础结构可以使用以下策略来解析查询。使用XML配置,您可以通过
query-lookup-strategy
属性在命名空间配置策略。对于Java配置,您可以使用注释的queryLookupStrategy
属性Enable${store}Repositories
。特定数据存储可能不支持某些策略。 -
CREATE
尝试从查询方法名称构造特定于商店的查询。一般方法是从方法名称中删除一组已知的前缀,并解析方法的其余部分。您可以在“ 查询创建 ”中阅读有关查询构造的更多信息。 -
USE_DECLARED_QUERY
尝试查找声明的查询,如果找不到,则抛出异常。查询可以通过某处的注释来定义,也可以通过其他方式声明。查阅特定商店的文档以查找该商店??的可用选项。如果存储库基础结构在引导时未找到该方法的声明查询,则它将失败。 -
CREATE_IF_NOT_FOUND
(默认)组合CREATE
和USE_DECLARED_QUERY
。它首先查找声明的查询,如果没有找到声明的查询,它会创建一个基于自定义方法名称的查询。这是默认的查找策略,因此,如果您未明确配置任何内容,则使用此策略。它允许通过方法名称快速查询,还可以根据需要引入声明的查询来自定义这些查询。 -
4.4.2。查询创建
构建到Spring Data存储库基础结构中的查询构建器机制对于构建对存储库实体的约束查询很有用。该机制条前缀
find…By
,read…By
,query…By
,count…By
,和get…By
从所述方法和开始分析它的其余部分。introduction子句可以包含其他表达式,例如Distinct
在要创建的查询上设置不同的标志。但是,第一个By
用作分隔符以指示实际标准的开始。在最基本的层面上,您可以在实体属性上定义条件,并将它们与And
和它们连接起来Or
。以下示例显示了如何创建大量查询:示例16.从方法名称创建查询
interface PersonRepository extends Repository<User, Long> {List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);// Enables the distinct flag for the queryList<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);// Enabling ignoring case for an individual propertyList<Person> findByLastnameIgnoreCase(String lastname);// Enabling ignoring case for all suitable propertiesList<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);// Enabling static ORDER BY for a queryList<Person> findByLastnameOrderByFirstnameAsc(String lastname);List<Person> findByLastnameOrderByFirstnameDesc(String lastname); }
解析方法的实际结果取决于您为其创建查询的持久性存储。但是,有一些一般要注意的事项:
-
表达式通常是属性遍历与可以连接的运算符相结合。您可以将属性表达式与
AND
和组合使用OR
。您还可以得到这样的运营商为支撑Between
,LessThan
,GreaterThan
,和Like
该属性的表达式。支持的运算符可能因数据存储而异,因此请参阅参考文档的相应部分。 -
方法解析器支持
IgnoreCase
为各个属性(例如findByLastnameIgnoreCase(…)
)或支持忽略大小写的类型(String
例如,通常为实例)的所有属性设置标志findByLastnameAndFirstnameAllIgnoreCase(…)
。是否支持忽略大小写可能因商店而异,因此请参阅参考文档中有关特定于商店的查询方法的相关章节。 -
您可以通过
OrderBy
在引用属性的查询方法中附加子句并提供排序方向(Asc
或Desc
)来应用静态排序。要创建支持动态排序的查询方法,请参阅“ 特殊参数处理 ”。 -
并非所有Spring Data模块目前都支持 Stream<T>
返回类型。
4.4.3。财产表达属性表达式只能引用被管实体的直接属性,如前面的示例所示。在创建查询时,您已确保已解析的属性是托管域类的属性。但是,您也可以通过遍历嵌套属性来定义约束。请考虑以下方法签名:
List<Person> findByAddressZipCode(ZipCode zipCode);
假设a
Person
有Address
aZipCode
。在这种情况下,该方法创建属性遍历x.address.zipCode
。解析算法首先将整个part(AddressZipCode
)解释为属性,并检查域类中是否具有该名称的属性(未大写)。如果算法成功,则使用该属性。如果没有,算法将来自右侧的驼峰案例部分的源分成头部和尾部,并尝试找到相应的属性 - 在我们的示例中,AddressZip
和Code
。如果算法找到具有该头部的属性,则它采用尾部并继续从那里构建树,以刚才描述的方式将尾部分开。如果第一个分割不匹配,算法会将分割点移动到左侧(Address
,ZipCode
)并继续。虽然这应该适用于大多数情况,但算法可能会选择错误的属性。假设
Person
该类也具有addressZip
属性。算法将在第一个拆分轮中匹配,选择错误的属性,并失败(因为类型addressZip
可能没有code
属性)。要解决此歧义,您可以
_
在方法名称内部使用手动定义遍历点。所以我们的方法名称如下:List<Person> findByAddress_ZipCode(ZipCode zipCode);
因为我们将下划线字符视为保留字符,所以我们强烈建议遵循标准Java命名约定(即,不在属性名称中使用下划线,而是使用camel case)。
4.4.4。特殊参数处理
要处理查询中的参数,请定义方法参数,如前面示例中所示。除此之外,基础设施承认某些特定的类型,如
Pageable
和Sort
,动态地应用分页和排序,以查询。以下示例演示了这些功能:示例17.使用
Pageable
,,Slice
和Sort
查询方法Page<User> findByLastname(String lastname, Pageable pageable);Slice<User> findByLastname(String lastname, Pageable pageable);List<User> findByLastname(String lastname, Sort sort);List<User> findByLastname(String lastname, Pageable pageable);
第一种方法允许您将
org.springframework.data.domain.Pageable
实例传递给查询方法,以动态地将分页添加到静态定义的查询中。APage
知道可用元素和页面的总数。它通过基础设施触发计数查询来计算总数来实现。由于这可能很昂贵(取决于所使用的商店),您可以改为返回Slice
。ASlice
只知道下一个Slice
是否可用,这在浏览更大的结果集时可能就足够了。排序选项也通过
Pageable
实例处理。如果只需要排序,org.springframework.data.domain.Sort
请在方法中添加参数。如您所见,返回aList
也是可能的。在这种情况下,Page
不会创建构建实际实例所需的其他元数据(这反过来意味着不会发出必要的附加计数查询)。相反,它限制查询仅查找给定范围的实体。要了解整个查询的页数,您必须触发额外的计数查询。默认情况下,此查询是从您实际触发的查询派生的。 4.4.5。限制查询结果
查询方法的结果可以通过使用
first
或top
关键字来限制,这些关键字可以互换使用。可以附加一个可选的数值,top
或者first
指定要返回的最大结果大小。如果省略该数字,则假定结果大小为1。以下示例显示如何限制查询大小:示例18.使用
Top
和限制查询的结果大小First
User findFirstByOrderByLastnameAsc();User findTopByOrderByAgeDesc();Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);Slice<User> findTop3ByLastname(String lastname, Pageable pageable);List<User> findFirst10ByLastname(String lastname, Sort sort);List<User> findTop10ByLastname(String lastname, Pageable pageable);
限制表达式也支持
Distinct
关键字。此外,对于将结果集限制为一个实例的查询,支持将结果包装到Optional
关键字中。如果将分页或切片应用于限制查询分页(以及可用页数的计算),则将其应用于有限结果中。
通过使用 Sort
参数将结果与动态排序结合使用,可以表达“K”最小元素和“K”元素的查询方法。4.4.6。流式查询结果
可以使用Java 8
Stream<T>
作为返回类型以递增方式处理查询方法的结果。而不是将查询结果包装在Stream
数据存储中特定的方法用于执行流式传输,如以下示例所示:示例19.使用Java 8流式传输查询结果
Stream<T>
@Query("select u from User u") Stream<User> findAllByCustomQueryAndStream();Stream<User> readAllByFirstnameNotNull();@Query("select u from User u") Stream<User> streamAllPaged(Pageable pageable);
甲 Stream
潜在包装底层数据存储专用资源,因此必须在使用之后被关闭。您可以Stream
使用该close()
方法或使用Java 7try-with-resources
块手动关闭,如以下示例所示:示例20.
Stream<T>
在try-with-resources块中使用结果try (Stream<User> stream = repository.findAllByCustomQueryAndStream()) {stream.forEach(…); }
4.4.7。异步查询结果
可以使用Spring的异步方法执行功能异步运行存储库查询。这意味着该方法在调用时立即返回,而实际的查询执行发生在已提交给Spring的任务中TaskExecutor
。异步查询执行与响应式查询执行不同,不应混合使用。有关反应支持的更多详细信息,请参阅特定于商店的文档。以下示例显示了许多异步查询:
@Async
Future<User> findByFirstname(String firstname); @Async
CompletableFuture<User> findOneByFirstname(String firstname); @Async
ListenableFuture<User> findOneByLastname(String lastname);
使用java.util.concurrent.Future 作为返回类型。 |
|
使用Java 8 java.util.concurrent.CompletableFuture 作为返回类型。 |
|
使用a org.springframework.util.concurrent.ListenableFuture 作为返回类型。 |
翻译自:https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.details