Browse Source

Polishing.

Reorder null-handling and collection return types in documentation to improve topical flow.

See #2804
pull/2782/merge
Mark Paluch 3 years ago
parent
commit
e1ec982e88
No known key found for this signature in database
GPG Key ID: 4406B84C1661DCD1
  1. 97
      src/main/asciidoc/repositories-null-handling.adoc
  2. 2
      src/main/asciidoc/repositories-paging-sorting.adoc
  3. 99
      src/main/asciidoc/repositories.adoc

97
src/main/asciidoc/repositories-null-handling.adoc

@ -0,0 +1,97 @@ @@ -0,0 +1,97 @@
[[repositories.nullability]]
=== Null Handling of Repository Methods
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`
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.
[[repositories.nullability.annotations]]
==== Nullability Annotations
You can express nullability constraints for repository methods by using {spring-framework-docs}/core.html#null-safety[Spring Framework's nullability annotations].
They provide a tooling-friendly approach and opt-in `null` checks during runtime, as follows:
* {spring-framework-javadoc}/org/springframework/lang/NonNullApi.html[`@NonNullApi`]: Used on the package level to declare that the default behavior for parameters and return values is, respectively, neither to accept nor to produce `null` values.
* {spring-framework-javadoc}/org/springframework/lang/NonNull.html[`@NonNull`]: Used on a parameter or return value that must not be `null` (not needed on a parameter and return value where `@NonNullApi` applies).
* {spring-framework-javadoc}/org/springframework/lang/Nullable.html[`@Nullable`]: Used on a parameter or return value that can be `null`.
Spring annotations are meta-annotated with https://jcp.org/en/jsr/detail?id=305[JSR 305] annotations (a dormant but widely used JSR).
JSR 305 meta-annotations let tooling vendors (such as https://www.jetbrains.com/help/idea/nullable-and-notnull-annotations.html[IDEA], https://help.eclipse.org/latest/index.jsp?topic=/org.eclipse.jdt.doc.user/tasks/task-using_external_null_annotations.htm[Eclipse], and link:https://kotlinlang.org/docs/reference/java-interop.html#null-safety-and-platform-types[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:
.Declaring Non-nullability in `package-info.java`
====
[source,java]
----
@org.springframework.lang.NonNullApi
package com.acme;
----
====
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:
.Using different nullability constraints
====
[source,java]
----
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`.
====
[[repositories.nullability.kotlin]]
==== Nullability in Kotlin-based Repositories
Kotlin has the definition of https://kotlinlang.org/docs/reference/null-safety.html[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:
.Using nullability constraints on Kotlin repositories
====
[source,kotlin]
----
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.
====

2
src/main/asciidoc/repositories-paging-sorting.adoc

@ -57,7 +57,6 @@ Rather, it restricts the query to look up only the given range of entities. @@ -57,7 +57,6 @@ Rather, it restricts the query to look up only the given range of entities.
NOTE: To find out how many pages you get for an entire query, you have to trigger an additional count query.
By default, this query is derived from the query you actually trigger.
[[repositories.scrolling.guidance]]
==== Which Method is Appropriate?
@ -178,7 +177,6 @@ ifdef::feature-scroll[] @@ -178,7 +177,6 @@ ifdef::feature-scroll[]
include::repositories-scrolling.adoc[]
endif::[]
[[repositories.limit-query-result]]
=== Limiting Query Results

99
src/main/asciidoc/repositories.adoc

@ -619,103 +619,6 @@ You can use the types in the first column (or subtypes thereof) as query method @@ -619,103 +619,6 @@ You can use the types in the first column (or subtypes thereof) as query method
Alternatively, you can declare `Traversable` (the Vavr `Iterable` equivalent), and we then derive the implementation class from the actual return value.
That is, a `java.util.List` is turned into a Vavr `List` or `Seq`, a `java.util.Set` becomes a Vavr `LinkedHashSet` `Set`, and so on.
[[repositories.nullability]]
=== Null Handling of Repository Methods
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`
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.
[[repositories.nullability.annotations]]
==== Nullability Annotations
You can express nullability constraints for repository methods by using {spring-framework-docs}/core.html#null-safety[Spring Framework's nullability annotations].
They provide a tooling-friendly approach and opt-in `null` checks during runtime, as follows:
* {spring-framework-javadoc}/org/springframework/lang/NonNullApi.html[`@NonNullApi`]: Used on the package level to declare that the default behavior for parameters and return values is, respectively, neither to accept nor to produce `null` values.
* {spring-framework-javadoc}/org/springframework/lang/NonNull.html[`@NonNull`]: Used on a parameter or return value that must not be `null` (not needed on a parameter and return value where `@NonNullApi` applies).
* {spring-framework-javadoc}/org/springframework/lang/Nullable.html[`@Nullable`]: Used on a parameter or return value that can be `null`.
Spring annotations are meta-annotated with https://jcp.org/en/jsr/detail?id=305[JSR 305] annotations (a dormant but widely used JSR).
JSR 305 meta-annotations let tooling vendors (such as https://www.jetbrains.com/help/idea/nullable-and-notnull-annotations.html[IDEA], https://help.eclipse.org/latest/index.jsp?topic=/org.eclipse.jdt.doc.user/tasks/task-using_external_null_annotations.htm[Eclipse], and link:https://kotlinlang.org/docs/reference/java-interop.html#null-safety-and-platform-types[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:
.Declaring Non-nullability in `package-info.java`
====
[source,java]
----
@org.springframework.lang.NonNullApi
package com.acme;
----
====
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:
.Using different nullability constraints
====
[source,java]
----
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`.
====
[[repositories.nullability.kotlin]]
==== Nullability in Kotlin-based Repositories
Kotlin has the definition of https://kotlinlang.org/docs/reference/null-safety.html[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:
.Using nullability constraints on Kotlin repositories
====
[source,kotlin]
----
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.
====
[[repositories.query-streaming]]
=== Streaming Query Results
@ -752,6 +655,8 @@ try (Stream<User> stream = repository.findAllByCustomQueryAndStream()) { @@ -752,6 +655,8 @@ try (Stream<User> stream = repository.findAllByCustomQueryAndStream()) {
NOTE: Not all Spring Data modules currently support `Stream<T>` as a return type.
include::repositories-null-handling.adoc[]
[[repositories.query-async]]
=== Asynchronous Query Results

Loading…
Cancel
Save