该模块通过 Pageable
和 Page
接口以及支持各种分页操作(如排序、分页和获得总页数)的实现来实现这一目标。它的设计旨在简化分页查询并使开发人员能够以一致的方式从不同的存储库中检索数据,无论底层持久性技术如何。
Null Handling of Repository Methods
从 Spring Data 2.0 开始,返回单个聚合实例的存储库 CRUD 方法使用 Java 8 的 Optional
来指示值可能不存在。除此之外,Spring Data 支持在查询方法上返回以下包装器类型:
As of Spring Data 2.0, repository CRUD methods that return an individual aggregate instance use Java 8’s Optional
to indicate the potential absence of a value.
Besides that, Spring Data supports returning the following wrapper types on query methods:
-
com.google.common.base.Optional
-
scala.Option
-
io.vavr.control.Option
或者,查询方法可以选择根本不使用包装器类型。然后通过返回 null
指示不存在查询结果。返回集合、集合替代项、包装器和流的存储库方法保证永不返回 null
,而是返回相应的空表示形式。有关详细信息,请参见 “Repository query return types”。
Alternatively, query methods can choose not to use a wrapper type at all.
The absence of a query result is then indicated by returning null
.
Repository methods returning collections, collection alternatives, wrappers, and streams are guaranteed never to return null
but rather the corresponding empty representation.
See “Repository query return types” for details.
Nullability Annotations
您可以使用 {spring-framework-docs}/core/null-safety.html[Spring Framework 的 NullPointerException 注释] 来表示存储库方法的空值约束。它们提供了一种对工具友好的方法,并在运行时选择加入 null
检查,如下所示:
You can express nullability constraints for repository methods by using {spring-framework-docs}/core/null-safety.html[Spring Framework’s nullability annotations].
They provide a tooling-friendly approach and opt-in null
checks during runtime, as follows:
-
add
: 在 package 级别使用,声明参数和返回值的默认行为分别不是接受或生成MyExtension
值。 -
@NonNullApi
: Used on the package level to declare that the default behavior for parameters and return values is, respectively, neither to accept nor to producenull
values. -
add
: 对于不能为MyExtension
的参数或返回值使用(在3
适用的参数和返回值上不需要)。 -
@NonNull
: Used on a parameter or return value that must not benull
(not needed on a parameter and return value where@NonNullApi
applies). -
@Nullable
:用于可以为null
的参数或返回值。 -
@Nullable
: Used on a parameter or return value that can benull
.
Spring 注释使用 JSR 305 注释进行元注释(一个休眠但广泛使用的 JSR)。JSR 305 元注释让工具供应商(比如 IDEA、 Eclipse 和 Kotlin)以通用方式提供空值安全性支持,而无需为 Spring 注释硬编码支持。要启用对查询方法的空值约束的运行时检查,您需要使用 Spring 的 @NonNullApi
在 package-info.java
中激活包级别的非空值,如以下示例所示:
Spring annotations are meta-annotated with JSR 305 annotations (a dormant but widely used JSR).
JSR 305 meta-annotations let tooling vendors (such as IDEA, Eclipse, and Kotlin) provide null-safety support in a generic way, without having to hard-code support for Spring annotations.
To enable runtime checking of nullability constraints for query methods, you need to activate non-nullability on the package level by using Spring’s @NonNullApi
in package-info.java
, as shown in the following example:
package-info.java
@org.springframework.lang.NonNullApi
package com.acme;
一旦启用非空默认值,存储库查询方法调用将在运行时针对空值约束进行验证。如果查询结果违反了已定义的约束,则会抛出异常。当方法返回 null
但声明为非空时(使用存储库所在的包中定义的注释时的默认设置),就会发生这种情况。如果你希望选择重新使用空值结果,请有选择地在各个方法上使用 @Nullable
。使用本节开头提到的结果包装器类型将继续按预期工作:空结果将转换为表示不存在的值。
Once non-null defaulting is in place, repository query method invocations get validated at runtime for nullability constraints.
If a query result violates the defined constraint, an exception is thrown.
This happens when the method would return null
but is declared as non-nullable (the default with the annotation defined on the package in which the repository resides).
If you want to opt-in to nullable results again, selectively use @Nullable
on individual methods.
Using the result wrapper types mentioned at the start of this section continues to work as expected: an empty result is translated into the value that represents absence.
以下示例显示了一些刚刚描述的技术:
The following example shows a number of the techniques just described:
package com.acme; 1
import org.springframework.lang.Nullable;
interface UserRepository extends Repository<User, Long> {
User getByEmailAddress(EmailAddress emailAddress); 2
@Nullable
User findByEmailAddress(@Nullable EmailAddress emailAdress); 3
Optional<User> findOptionalByEmailAddress(EmailAddress emailAddress); 4
}
1 | 存储库位于我们已定义非空行为的包(或子包)中。 |
2 | The repository resides in a package (or sub-package) for which we have defined non-null behavior. |
3 | 当查询没有产生结果时,抛出 EmptyResultDataAccessException 。当传递给方法的 emailAddress 为 null 时,抛出 IllegalArgumentException 。 |
4 | Throws an EmptyResultDataAccessException when the query does not produce a result.
Throws an IllegalArgumentException when the emailAddress handed to the method is null . |
5 | 当查询没有产生结果时,返回 null 。还接受 null 作为 emailAddress 的值。 |
6 | Returns null when the query does not produce a result.
Also accepts null as the value for emailAddress . |
7 | 当查询没有产生结果时,返回 Optional.empty() 。当传递给方法的 emailAddress 为 null 时,抛出 IllegalArgumentException 。 |
8 | Returns Optional.empty() when the query does not produce a result.
Throws an IllegalArgumentException when the emailAddress handed to the method is null . |
Nullability in Kotlin-based Repositories
Kotlin 语言本身就定义了 nullability constraints。Kotlin 代码编译为字节码,字节码不会通过方法签名来表现空安全约束,而是通过编译的元数据。确保将 kotlin-reflect
JAR 包含在项目中,以允许内省 Kotlin 的空安全约束。Spring Data 存储库使用语言机制来定义那些约束,从而应用同样的运行时检查,如下所示:
Kotlin has the definition of nullability constraints baked into the language.
Kotlin code compiles to bytecode, which does not express nullability constraints through method signatures but rather through compiled-in metadata.
Make sure to include the kotlin-reflect
JAR in your project to enable introspection of Kotlin’s nullability constraints.
Spring Data repositories use the language mechanism to define those constraints to apply the same runtime checks, as follows:
interface UserRepository : Repository<User, String> {
fun findByUsername(username: String): User 1
fun findByFirstname(firstname: String?): User? 2
}
1 | 此方法将参数和结果都定义为非空(Kotlin 默认值)。Kotlin 编译器拒绝将 null 传递给方法的方法调用。如果查询产生空结果,则抛出 EmptyResultDataAccessException 。 |
2 | The method defines both the parameter and the result as non-nullable (the Kotlin default).
The Kotlin compiler rejects method invocations that pass null to the method.
If the query yields an empty result, an EmptyResultDataAccessException is thrown. |
3 | 此方法接受 null 作为 firstname 参数,并且如果查询没有产生结果,则返回 null 。 |
4 | This method accepts null for the firstname parameter and returns null if the query does not produce a result. |