Skip to content

Commit e1ec982

Browse files
committed
Polishing.
Reorder null-handling and collection return types in documentation to improve topical flow. See #2804
1 parent f903ddc commit e1ec982

File tree

3 files changed

+99
-99
lines changed

3 files changed

+99
-99
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
[[repositories.nullability]]
2+
=== Null Handling of Repository Methods
3+
4+
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.
5+
Besides that, Spring Data supports returning the following wrapper types on query methods:
6+
7+
* `com.google.common.base.Optional`
8+
* `scala.Option`
9+
* `io.vavr.control.Option`
10+
11+
Alternatively, query methods can choose not to use a wrapper type at all.
12+
The absence of a query result is then indicated by returning `null`.
13+
Repository methods returning collections, collection alternatives, wrappers, and streams are guaranteed never to return `null` but rather the corresponding empty representation.
14+
See "`<<repository-query-return-types>>`" for details.
15+
16+
[[repositories.nullability.annotations]]
17+
==== Nullability Annotations
18+
19+
You can express nullability constraints for repository methods by using {spring-framework-docs}/core.html#null-safety[Spring Framework's nullability annotations].
20+
They provide a tooling-friendly approach and opt-in `null` checks during runtime, as follows:
21+
22+
* {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.
23+
* {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).
24+
* {spring-framework-javadoc}/org/springframework/lang/Nullable.html[`@Nullable`]: Used on a parameter or return value that can be `null`.
25+
26+
Spring annotations are meta-annotated with https://jcp.org/en/jsr/detail?id=305[JSR 305] annotations (a dormant but widely used JSR).
27+
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.
28+
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:
29+
30+
.Declaring Non-nullability in `package-info.java`
31+
====
32+
[source,java]
33+
----
34+
@org.springframework.lang.NonNullApi
35+
package com.acme;
36+
----
37+
====
38+
39+
Once non-null defaulting is in place, repository query method invocations get validated at runtime for nullability constraints.
40+
If a query result violates the defined constraint, an exception is thrown.
41+
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).
42+
If you want to opt-in to nullable results again, selectively use `@Nullable` on individual methods.
43+
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.
44+
45+
The following example shows a number of the techniques just described:
46+
47+
.Using different nullability constraints
48+
====
49+
[source,java]
50+
----
51+
package com.acme; <1>
52+
53+
import org.springframework.lang.Nullable;
54+
55+
interface UserRepository extends Repository<User, Long> {
56+
57+
User getByEmailAddress(EmailAddress emailAddress); <2>
58+
59+
@Nullable
60+
User findByEmailAddress(@Nullable EmailAddress emailAdress); <3>
61+
62+
Optional<User> findOptionalByEmailAddress(EmailAddress emailAddress); <4>
63+
}
64+
----
65+
<1> The repository resides in a package (or sub-package) for which we have defined non-null behavior.
66+
<2> Throws an `EmptyResultDataAccessException` when the query does not produce a result.
67+
Throws an `IllegalArgumentException` when the `emailAddress` handed to the method is `null`.
68+
<3> Returns `null` when the query does not produce a result.
69+
Also accepts `null` as the value for `emailAddress`.
70+
<4> Returns `Optional.empty()` when the query does not produce a result.
71+
Throws an `IllegalArgumentException` when the `emailAddress` handed to the method is `null`.
72+
====
73+
74+
[[repositories.nullability.kotlin]]
75+
==== Nullability in Kotlin-based Repositories
76+
77+
Kotlin has the definition of https://kotlinlang.org/docs/reference/null-safety.html[nullability constraints] baked into the language.
78+
Kotlin code compiles to bytecode, which does not express nullability constraints through method signatures but rather through compiled-in metadata.
79+
Make sure to include the `kotlin-reflect` JAR in your project to enable introspection of Kotlin's nullability constraints.
80+
Spring Data repositories use the language mechanism to define those constraints to apply the same runtime checks, as follows:
81+
82+
.Using nullability constraints on Kotlin repositories
83+
====
84+
[source,kotlin]
85+
----
86+
interface UserRepository : Repository<User, String> {
87+
88+
fun findByUsername(username: String): User <1>
89+
90+
fun findByFirstname(firstname: String?): User? <2>
91+
}
92+
----
93+
<1> The method defines both the parameter and the result as non-nullable (the Kotlin default).
94+
The Kotlin compiler rejects method invocations that pass `null` to the method.
95+
If the query yields an empty result, an `EmptyResultDataAccessException` is thrown.
96+
<2> This method accepts `null` for the `firstname` parameter and returns `null` if the query does not produce a result.
97+
====

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

-2
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ Rather, it restricts the query to look up only the given range of entities.
5757
NOTE: To find out how many pages you get for an entire query, you have to trigger an additional count query.
5858
By default, this query is derived from the query you actually trigger.
5959

60-
6160
[[repositories.scrolling.guidance]]
6261
==== Which Method is Appropriate?
6362

@@ -178,7 +177,6 @@ ifdef::feature-scroll[]
178177
include::repositories-scrolling.adoc[]
179178
endif::[]
180179

181-
182180
[[repositories.limit-query-result]]
183181
=== Limiting Query Results
184182

src/main/asciidoc/repositories.adoc

+2-97
Original file line numberDiff line numberDiff line change
@@ -619,103 +619,6 @@ You can use the types in the first column (or subtypes thereof) as query method
619619
Alternatively, you can declare `Traversable` (the Vavr `Iterable` equivalent), and we then derive the implementation class from the actual return value.
620620
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.
621621

622-
[[repositories.nullability]]
623-
=== Null Handling of Repository Methods
624-
625-
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.
626-
Besides that, Spring Data supports returning the following wrapper types on query methods:
627-
628-
* `com.google.common.base.Optional`
629-
* `scala.Option`
630-
* `io.vavr.control.Option`
631-
632-
Alternatively, query methods can choose not to use a wrapper type at all.
633-
The absence of a query result is then indicated by returning `null`.
634-
Repository methods returning collections, collection alternatives, wrappers, and streams are guaranteed never to return `null` but rather the corresponding empty representation.
635-
See "`<<repository-query-return-types>>`" for details.
636-
637-
[[repositories.nullability.annotations]]
638-
==== Nullability Annotations
639-
640-
You can express nullability constraints for repository methods by using {spring-framework-docs}/core.html#null-safety[Spring Framework's nullability annotations].
641-
They provide a tooling-friendly approach and opt-in `null` checks during runtime, as follows:
642-
643-
* {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.
644-
* {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).
645-
* {spring-framework-javadoc}/org/springframework/lang/Nullable.html[`@Nullable`]: Used on a parameter or return value that can be `null`.
646-
647-
Spring annotations are meta-annotated with https://jcp.org/en/jsr/detail?id=305[JSR 305] annotations (a dormant but widely used JSR).
648-
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.
649-
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:
650-
651-
.Declaring Non-nullability in `package-info.java`
652-
====
653-
[source,java]
654-
----
655-
@org.springframework.lang.NonNullApi
656-
package com.acme;
657-
----
658-
====
659-
660-
Once non-null defaulting is in place, repository query method invocations get validated at runtime for nullability constraints.
661-
If a query result violates the defined constraint, an exception is thrown.
662-
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).
663-
If you want to opt-in to nullable results again, selectively use `@Nullable` on individual methods.
664-
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.
665-
666-
The following example shows a number of the techniques just described:
667-
668-
.Using different nullability constraints
669-
====
670-
[source,java]
671-
----
672-
package com.acme; <1>
673-
674-
import org.springframework.lang.Nullable;
675-
676-
interface UserRepository extends Repository<User, Long> {
677-
678-
User getByEmailAddress(EmailAddress emailAddress); <2>
679-
680-
@Nullable
681-
User findByEmailAddress(@Nullable EmailAddress emailAdress); <3>
682-
683-
Optional<User> findOptionalByEmailAddress(EmailAddress emailAddress); <4>
684-
}
685-
----
686-
<1> The repository resides in a package (or sub-package) for which we have defined non-null behavior.
687-
<2> Throws an `EmptyResultDataAccessException` when the query does not produce a result.
688-
Throws an `IllegalArgumentException` when the `emailAddress` handed to the method is `null`.
689-
<3> Returns `null` when the query does not produce a result.
690-
Also accepts `null` as the value for `emailAddress`.
691-
<4> Returns `Optional.empty()` when the query does not produce a result.
692-
Throws an `IllegalArgumentException` when the `emailAddress` handed to the method is `null`.
693-
====
694-
695-
[[repositories.nullability.kotlin]]
696-
==== Nullability in Kotlin-based Repositories
697-
698-
Kotlin has the definition of https://kotlinlang.org/docs/reference/null-safety.html[nullability constraints] baked into the language.
699-
Kotlin code compiles to bytecode, which does not express nullability constraints through method signatures but rather through compiled-in metadata.
700-
Make sure to include the `kotlin-reflect` JAR in your project to enable introspection of Kotlin's nullability constraints.
701-
Spring Data repositories use the language mechanism to define those constraints to apply the same runtime checks, as follows:
702-
703-
.Using nullability constraints on Kotlin repositories
704-
====
705-
[source,kotlin]
706-
----
707-
interface UserRepository : Repository<User, String> {
708-
709-
fun findByUsername(username: String): User <1>
710-
711-
fun findByFirstname(firstname: String?): User? <2>
712-
}
713-
----
714-
<1> The method defines both the parameter and the result as non-nullable (the Kotlin default).
715-
The Kotlin compiler rejects method invocations that pass `null` to the method.
716-
If the query yields an empty result, an `EmptyResultDataAccessException` is thrown.
717-
<2> This method accepts `null` for the `firstname` parameter and returns `null` if the query does not produce a result.
718-
====
719622

720623
[[repositories.query-streaming]]
721624
=== Streaming Query Results
@@ -752,6 +655,8 @@ try (Stream<User> stream = repository.findAllByCustomQueryAndStream()) {
752655

753656
NOTE: Not all Spring Data modules currently support `Stream<T>` as a return type.
754657

658+
include::repositories-null-handling.adoc[]
659+
755660
[[repositories.query-async]]
756661
=== Asynchronous Query Results
757662

0 commit comments

Comments
 (0)