Skip to content

Commit d7bfa82

Browse files
committed
DATACMNS-1190 - Polishing.
Remove existing section on Null-safety. Augment Null handling section with bits of the previous documentation, explicitly describe annotations expressing null-constraints, include full-qualified imports, mention kotlin-reflect dependency for Kotlin metadata introspection. Align callouts, add links, slightly improve wording, typos. Extend year range in copyright header.
1 parent 1f12660 commit d7bfa82

File tree

2 files changed

+38
-81
lines changed

2 files changed

+38
-81
lines changed

Diff for: src/main/asciidoc/index.adoc

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
= Spring Data Commons - Reference Documentation
2-
Oliver Gierke; Thomas Darimont; Christoph Strobl; Mark Pollack; Thomas Risberg;
2+
Oliver Gierke; Thomas Darimont; Christoph Strobl; Mark Pollack; Thomas Risberg; Mark Paluch;
33
:revnumber: {version}
44
:revdate: {localdate}
55
:toc:
66
:toc-placement!:
77

8-
(C) 2008-2015 The original authors.
8+
(C) 2008-2017 The original authors.
99

1010
NOTE: Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.
1111

Diff for: src/main/asciidoc/repositories.adoc

+36-79
Original file line numberDiff line numberDiff line change
@@ -227,25 +227,41 @@ Besides that, Spring Data supports to return other wrapper types on query method
227227

228228
Alternatively query methods can choose not to use a wrapper type at all.
229229
The absence of a query result will then be indicated by returning `null`.
230-
Repository methods returning collection like values will never return null but an empty value.
230+
Repository methods returning collections, wrappers, and streams are guaranteed never to return `null` but rather the corresponding empty representation.
231231

232232
[[repositories.nullability.annotations]]
233233
==== Nullability annotations
234234

235-
To properly validate nullability constraints on a repository method at runtime, annotations can be defined to define whether query methods are supposed to return `null`.
236-
To enable this, non-nullability needs to be activated on the package level, e.g. using Spring's `@NonNullApi` in `package-info.java`:
235+
You can express null-safe repository methods by using link:{spring-framework-docs}core.html#null-safety[Spring Framework's annotations].
236+
They provide a tooling-friendly approach and opt-in for `null` checks during runtime:
237237

238-
.Declaring non-nullablity in `package-info.java`
238+
* https://docs.spring.io/spring-framework/docs/{springVersion}/javadoc-api/org/springframework/lang/NonNull.html[`@NonNull`]
239+
annotation where a specific parameter or return value cannot be `null`
240+
(not needed on parameter and return value where `@NonNullApi` applies).
241+
242+
* https://docs.spring.io/spring-framework/docs/{springVersion}/javadoc-api/org/springframework/lang/Nullable.html[`@Nullable`]
243+
annotation where a specific parameter or return value can be `null`.
244+
245+
* https://docs.spring.io/spring-framework/docs/{springVersion}/javadoc-api/org/springframework/lang/NonNullApi.html[`@NonNullApi`]
246+
annotation at package level declares non-null as the default behavior for parameters and return values.
247+
248+
Spring annotations are meta-annotated with https://jcp.org/en/jsr/detail?id=305[JSR 305] annotations (a dormant but widely spread JSR). JSR 305 meta-annotations allow tooling vendors like https://www.jetbrains.com/help/idea/nullable-and-notnull-annotations.html[IDEA], http://help.eclipse.org/oxygen/index.jsp?topic=/org.eclipse.jdt.doc.user/tasks/task-using_external_null_annotations.htm[Eclipse], or link:https://kotlinlang.org/docs/reference/java-interop.html#null-safety-and-platform-types[Kotlin] to provide null-safety support in a generic way, without having to hard-code support for Spring annotations.
249+
250+
To enable runtime checking of nullability constraints for query methods, you need to activate non-nullability on package level using Spring’s `@NonNullApi` in `package-info.java`:
251+
252+
.Declaring non-nullability in `package-info.java`
239253
====
240254
[source, java]
241255
----
242-
@NonNullApi
256+
@org.springframework.lang.NonNullApi
243257
package com.acme;
244258
----
245259
====
246260

247-
Once that is in place, repository query method will get runtime execution validation of the nullability constraints and exceptions will be thrown in case a query execution result violates the defined constrained, i.e. the method would return `null` for some reason but is declared as non-nullable (the default with the annotation defined on the package the repository resides in).
248-
If you want to opt-in to nullable results again, e.g. `@Nullable` can be used on a method selectively.
261+
Once non-null defaulting is in place, repository query method invocations will get validated at runtime for nullability constraints.
262+
Exceptions will be thrown in case a query execution result violates the defined constraint, i.e. the method would return `null` for some reason but is declared as non-nullable (the default with the annotation defined on the package the repository resides in).
263+
If you want to opt-in to nullable results again, use selectively `@Nullable` on a method.
264+
249265
Using the aforementioned result wrapper types will continue to work as expected, i.e. an empty result will be translated into the value representing absence.
250266

251267
.Using different nullability constraints
@@ -254,12 +270,14 @@ Using the aforementioned result wrapper types will continue to work as expected,
254270
----
255271
package com.acme;
256272
273+
import org.springframework.lang.Nullable;
274+
257275
interface UserRepository extends Repository<User, Long> {
258276
259-
User getByEmailAddress(EmailAddress emailAddress); <1>
277+
User getByEmailAddress(EmailAddress emailAddress); <1>
260278
261279
@Nullable
262-
User findByEmailAddess(@Nullable EmailAddress emailAdress); <2>
280+
User findByEmailAddess(@Nullable EmailAddress emailAdress); <2>
263281
264282
Optional<User> findOptionalByEmailAddress(EmailAddress emailAddress); <3>
265283
}
@@ -272,24 +290,27 @@ interface UserRepository extends Repository<User, Long> {
272290
[[repositories.nullability.kotlin]]
273291
==== Nullability in Kotlin-based repositories
274292

275-
Kotlin has the definition of nullability constraints baked into the language.
276-
Spring Data repositories using the language mechanism to define those constraints will automatically get the same runtime checks applied:
293+
Kotlin has the definition of https://kotlinlang.org/docs/reference/null-safety.html[nullability constraints]
294+
baked into the language. Spring Data repositories use the language mechanism
295+
to define those constraints to apply the same runtime checks:
277296

278297
.Using nullability constraints on Kotlin repositories
279298
====
280299
[source, kotlin]
281300
----
282301
interface UserRepository : Repository<User, String> {
283302
284-
fun findByUsername(username: String): User <1>
303+
fun findByUsername(username: String): User <1>
285304
286-
fun findByFirstname(firstname: String?): User? <2>
305+
fun findByFirstname(firstname: String?): User? <2>
287306
}
288307
----
289-
<1> The method defines both the parameter as non-nullable (the Kotlin default) as well as the result. The Kotlin compiler will already reject method invocations trying to hand `null` into the method. In case the query execution yields an empty result, an `EmptyResultDataAccessException` will be thrown.
290-
<2> This method accepts `null` as parameter for `firstname` and return `null` in case the query execution does not produce a result.
308+
<1> The method defines both, the parameter as non-nullable (the Kotlin default) as well as the result. The Kotlin compiler will already reject method invocations trying to hand `null` into the method. In case the query execution yields an empty result, an `EmptyResultDataAccessException` will be thrown.
309+
<2> This method accepts `null` as parameter for `firstname` and returns `null` in case the query execution does not produce a result.
291310
====
292311

312+
Kotlin code compiles to bytecode which does not express nullability constraints using method signatures but rather compiled-in metadata. Make sure to include `kotlin-reflect` to enable introspection of Kotlin's nullability constraints.
313+
293314
[[repositories.multiple-modules]]
294315
=== Using Repositories with multiple Spring Data modules
295316

@@ -942,70 +963,6 @@ class AnAggregateRoot {
942963

943964
The methods will be called every time one of a Spring Data repository's `save(…)` methods is called.
944965

945-
[[core.nullability-validation]]
946-
== Null-safety
947-
948-
Repository methods let you improve null-safety to deal with `null` values at compile time rather than bumping into the famous `NullPointerException` at runtime. This makes your applications safer through clean nullability declarations, expressing "value or no value" semantics without paying the cost of a wrapper like `Optional`.
949-
950-
You can express null-safe repository methods by using Spring Framework's annotations. They provide a tooling-friendly approach and opt-in for `null` checks during runtime:
951-
952-
* `@NonNullApi` annotations at package level declare non-null as the default behavior
953-
954-
* `@Nullable` annotations where specific parameters or return values can be `null`.
955-
956-
Both annotations are meta-annotated with https://jcp.org/en/jsr/detail?id=305[JSR-305] meta-annotations (a dormant JSR but supported by tools like IDEA, Eclipse, Findbugs, etc.) to provide useful warnings to Java developers.
957-
958-
Make sure to include a JAR file containing JSR-305's `@Nonnull` annotation on your class path if you intend to use own meta-annotations.
959-
960-
NOTE: Invocations of repository query methods in the scope of null-declarations, either declared on package-level or with Kotlin, are validated during runtime. Passing a `null` value to a query method parameter that is not-nullable is rejected with an exception. A query method that yields no result and is not-nullable throws `EmptyResultDataAccessException` instead of returning `null`.
961-
962-
.Activating non-null defaults for a package
963-
====
964-
[source, java]
965-
----
966-
@org.springframework.lang.NonNullApi
967-
package com.example;
968-
----
969-
====
970-
971-
.Declaring nullability for parameters and return values
972-
====
973-
[source, java]
974-
----
975-
package com.example; <1>
976-
977-
interface UserRepository extends Repository<User, String> {
978-
979-
List<User> findByLastname(@Nullable String firstname); <2>
980-
981-
@Nullable
982-
User findByFirstnameAndLastname(String firstname, String lastname); <3>
983-
}
984-
----
985-
<1> `@NonNullApi` on package-level declares that all API within this package
986-
defaults to non-null.
987-
<2> `@Nullable` allows `null` usage on particular parameters. Each nullable parameter
988-
must be annotated.
989-
<3> Methods that may return `null` are annotated with `@Nullable`.
990-
====
991-
992-
If you declare your repository interfaces with Kotlin, then you can use Kotlin's https://kotlinlang.org/docs/reference/null-safety.html[null-safety] to express nullability.
993-
994-
.Declaring nullability for parameters and return values in Kotlin
995-
====
996-
[source, java]
997-
----
998-
interface UserRepository : Repository<User, String> {
999-
1000-
fun findByLastname(username: String?): List<User>
1001-
1002-
fun findByFirstnameAndLastname(firstname: String, lastname: String): User?
1003-
}
1004-
----
1005-
====
1006-
1007-
NOTE: Kotlin code compiles to bytecode which does not express nullability declarations using method signatures but rather compiled-in metadata. Make sure to include `kotlin-reflect` to enable introspection of Kotlin's nullability declarations.
1008-
1009966
[[core.extensions]]
1010967
== Spring Data extensions
1011968

0 commit comments

Comments
 (0)