リポジトリメソッドの null 処理
Spring Data 2.0 以降、個々の集約インスタンスを返すリポジトリ CRUD メソッドは、Java 8 の Optional
を使用して、値が存在しない可能性があることを示します。さらに、Spring Data はクエリメソッドで次のラッパー型を返すことをサポートしています。
com.google.common.base.Optional
scala.Option
io.vavr.control.Option
または、クエリメソッドは、ラッパー型をまったく使用しないことを選択できます。null
を返すことにより、クエリ結果がないことが示されます。コレクション、コレクションの代替、ラッパー、ストリームを返すリポジトリメソッドは、null
ではなく、対応する空の表現を返すことが保証されています。詳細については、"リポジトリクエリの戻り値の型" を参照してください。
Nullability アノテーション
Spring Framework の nullability アノテーションを使用して、リポジトリメソッドの nullability 制約を表現できます。これらは、次のように、実行時にツールに優しいアプローチとオプトイン null
チェックを提供します。
@NonNullApi
(Javadoc) : パッケージレベルで使用され、パラメーターと戻り値のデフォルトの動作が、それぞれnull
値を受け入れることも生成することもないことを宣言します。@NonNull
(Javadoc) :null
であってはならないパラメーターまたは戻り値で使用されます(@NonNullApi
が適用されるパラメーターおよび戻り値では不要です)。@Nullable
(Javadoc) :null
の可能性があるパラメーターまたは戻り値で使用されます。
Spring アノテーションは、JSR 305 (英語) アノテーション(休止中ですが広く使用されている JSR)でメタアノテーションが付けられています。JSR 305 メタアノテーションにより、ツールベンダー(IDEA (英語) 、Eclipse (英語) 、Kotlin (英語) など)は、Spring アノテーションのサポートをハードコードすることなく、一般的な方法で null-safety サポートを提供できます。クエリメソッドの null 可能性制約のランタイムチェックを有効にするには、次の例に示すように、package-info.java
で Spring の @NonNullApi
を使用して、パッケージレベルで非 null 可能性をアクティブ化する必要があります。
package-info.java
で非 null 可能性を宣言する @org.springframework.lang.NonNullApi
package com.acme;
null 以外のデフォルトが設定されると、リポジトリクエリメソッドの呼び出しは、実行時に null 可能性の制約について検証されます。クエリ結果が定義された制約に違反している場合、例外がスローされます。これは、メソッドが null
を返すが、null 許容ではないと宣言されている場合に発生します(リポジトリが存在するパッケージで定義されたアノテーションのデフォルト)。null 許容の結果に再度オプトインする場合は、個々のメソッドで @Nullable
を選択的に使用します。このセクションの冒頭で説明した結果ラッパー型を使用すると、引き続き期待どおりに機能します。空の結果は、不在を表す値に変換されます。
次の例は、今説明したいくつかの手法を示しています。
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 | リポジトリは、null 以外の動作を定義したパッケージ(またはサブパッケージ)にあります。 |
2 | クエリで結果が生成されない場合は、EmptyResultDataAccessException をスローします。メソッドに渡された emailAddress が null の場合、IllegalArgumentException をスローします。 |
3 | クエリが結果を生成しない場合、null を返します。emailAddress の値として null も受け入れます。 |
4 | クエリが結果を生成しない場合、Optional.empty() を返します。メソッドに渡された emailAddress が null の場合、IllegalArgumentException をスローします。 |
Kotlin ベースのリポジトリの Nullability
Kotlin には、言語に組み込まれた null 可能性制約 (英語) の定義があります。Kotlin コードはバイトコードにコンパイルされます。これは、メソッドシグネチャーではなく、コンパイルされたメタデータを通じて nullability 制約を表現しません。kotlin-reflect
JAR をプロジェクトに含めて、Kotlin の nullability 制約のイントロスペクションを有効にしてください。Spring Data リポジトリは、言語メカニズムを使用してこれらの制約を定義し、次のように同じランタイムチェックを適用します。
interface UserRepository : Repository<User, String> {
fun findByUsername(username: String): User (1)
fun findByFirstname(firstname: String?): User? (2)
}
1 | このメソッドは、パラメーターと結果の両方を null 不可(Kotlin のデフォルト)として定義します。Kotlin コンパイラーは、null をメソッドに渡すメソッド呼び出しを拒否します。クエリで空の結果が得られた場合、EmptyResultDataAccessException がスローされます。 |
2 | このメソッドは、firstname パラメーターに null を受け入れ、クエリで結果が生成されない場合は null を返します。 |