, LIMIT, QuerydslPredicateExecutor
:description: 这篇文章的核心论点是,在 Spring Data Commons 的分页 API 中,null
值应被明确地处理,以避免意外行为。该文章建议在进行分页操作之前检查 null
值,并根据需要采取适当的措施,例如返回空页或抛出异常。这样可以确保分页操作的行为符合预期,并有助于防止数据完整性问题。文章还提供了处理 null
值的代码示例,以说明如何在 Spring Data Commons 中正确实施此建议。
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:
-
@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. -
@NonNull
: Used on a parameter or return value that must not benull
(not needed on a parameter and return value where@NonNullApi
applies). -
@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 | The repository resides in a package (or sub-package) for which we have defined non-null behavior. |
2 | Throws an EmptyResultDataAccessException when the query does not produce a result.
Throws an IllegalArgumentException when the emailAddress handed to the method is null . |
3 | Returns null when the query does not produce a result.
Also accepts null as the value for emailAddress . |
4 | 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 | 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. |
2 | This method accepts null for the firstname parameter and returns null if the query does not produce a result. |