Skip to content

Commit 1f12660

Browse files
committed
DATACMNS-1190 - Added section on how to enforce nullability constraints on repositories.
Original pull request: #253.
1 parent 3751b70 commit 1f12660

File tree

1 file changed

+76
-0
lines changed

1 file changed

+76
-0
lines changed

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

+76
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,82 @@ In this first step you defined a common base interface for all your domain repos
214214
NOTE: Note, that the intermediate repository interface is annotated with `@NoRepositoryBean`. Make sure you add that annotation to all repository interfaces that Spring Data should not create instances for at runtime.
215215

216216

217+
[[repositories.nullability]]
218+
=== Null handling of repository methods
219+
220+
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.
221+
Besides that, Spring Data supports to return other wrapper types on query methods:
222+
223+
* `com.google.common.base.Optional`
224+
* `scala.Option`
225+
* `io.vavr.control.Option`
226+
* `javaslang.control.Option` (deprecated as Javaslang is deprecated)
227+
228+
Alternatively query methods can choose not to use a wrapper type at all.
229+
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.
231+
232+
[[repositories.nullability.annotations]]
233+
==== Nullability annotations
234+
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`:
237+
238+
.Declaring non-nullablity in `package-info.java`
239+
====
240+
[source, java]
241+
----
242+
@NonNullApi
243+
package com.acme;
244+
----
245+
====
246+
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.
249+
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.
250+
251+
.Using different nullability constraints
252+
====
253+
[source, java]
254+
----
255+
package com.acme;
256+
257+
interface UserRepository extends Repository<User, Long> {
258+
259+
User getByEmailAddress(EmailAddress emailAddress); <1>
260+
261+
@Nullable
262+
User findByEmailAddess(@Nullable EmailAddress emailAdress); <2>
263+
264+
Optional<User> findOptionalByEmailAddress(EmailAddress emailAddress); <3>
265+
}
266+
----
267+
<1> Will throw an `EmptyResultDataAccessException` in case the query executed does not produce a result. Will throw an `IllegalArgumentException` in case the `emailAddress` handed to the method is `null`.
268+
<2> Will return `null` in case the query executed does not produce a result. Also accepts `null` as value for `emailAddress`.
269+
<3> Will return `Optional.empty()` in case the query executed does not produce a result. Will throw an `IllegalArgumentException` in case the `emailAddress` handed to the method is `null`.
270+
====
271+
272+
[[repositories.nullability.kotlin]]
273+
==== Nullability in Kotlin-based repositories
274+
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:
277+
278+
.Using nullability constraints on Kotlin repositories
279+
====
280+
[source, kotlin]
281+
----
282+
interface UserRepository : Repository<User, String> {
283+
284+
fun findByUsername(username: String): User <1>
285+
286+
fun findByFirstname(firstname: String?): User? <2>
287+
}
288+
----
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.
291+
====
292+
217293
[[repositories.multiple-modules]]
218294
=== Using Repositories with multiple Spring Data modules
219295

0 commit comments

Comments
 (0)