このバージョンはまだ開発中であり、まだ安定しているとは見なされていません。最新の安定バージョンについては、Spring Data Commons 3.4.5 を使用してください!

リポジトリメソッドの null 処理

個々の集約インスタンスを返すリポジトリ CRUD メソッドは、Optional を使用して値が存在しない可能性があることを示すことができます。さらに、Spring Data はクエリメソッドで次のラッパー型を返すことをサポートしています。

  • com.google.common.base.Optional

  • scala.Option

  • io.vavr.control.Option

または、クエリメソッドは、ラッパー型をまったく使用しないことを選択できます。null を返すことにより、クエリ結果がないことが示されます。コレクション、コレクションの代替、ラッパー、ストリームを返すリポジトリメソッドは、null ではなく、対応する空の表現を返すことが保証されています。詳細については、"リポジトリクエリの戻り値の型" を参照してください。

Nullability アノテーション

JSpecify

Spring Framework 7 および Spring Data 4 と同様に、JSpecify (英語) を使用してリポジトリメソッドの null 可能性制約を表現できます。JSpecify は IntelliJ および Eclipse に適切に統合されており、次のようにツールに適したアプローチを提供し、実行時に null チェックをオプトインします。

  • @NullMarked (英語) : モジュール、パッケージ、クラスレベルで使用され、パラメーターと戻り値のデフォルトの動作がそれぞれ null 値を受け入れず、生成もしないことを宣言します。

  • @NonNull (英語) null であってはならないパラメーターまたは戻り値 (@NullMarked が適用される場合は値は必要ありません) の型レベルで使用されます。

  • @Nullable (英語) null にできるパラメーターまたは戻り値の型レベルで使用されます。

  • @NullUnmarked (英語) : パッケージ、クラス、メソッドレベルで使用され、null 宣言をロールバックし、以前の @NullMarked からオプトアウトします。このような場合、null は未指定に変更されます。

 package-info.java ファイル経由のパッケージレベルの @NullMarked 
@NullMarked
package org.springframework.core;

import org.jspecify.annotations.NullMarked;

パッケージに属するさまざまな Java ファイルでは、null 許容型の使用箇所が @Nullable (英語) で明示的に定義されています。このアノテーションは、関連する型の直前に指定することをお勧めします。

例: フィールドの場合:

private @Nullable String fileEncoding;

または、メソッドのパラメーターと戻り値の場合:

public static @Nullable String buildMessage(@Nullable String message,
                                            @Nullable Throwable cause) {
    // ...
}

メソッドをオーバーライドする場合、null 性アノテーションはスーパークラスのメソッドから継承されません。つまり、実装をオーバーライドして同じ API null 性を維持したいだけの場合は、これらの null 性アノテーションを繰り返す必要があります。

配列と可変引数を使用する場合、要素の null と配列自体の null を区別できる必要があります。最初は驚くかもしれませんが、Java 仕様で定義されている [Oracle] (英語) 構文に注意してください。

  • @Nullable Object[] array は、個々の要素は null にできるが、配列自体は null にできないことを意味します。

  • Object @Nullable[] array は、個々の要素は null にできないが、配列自体は null にできることを意味します。

  • @Nullable Object @Nullable[] array は、個々の要素と配列の両方が null になる可能性があることを意味します。

Java 仕様では、JSpecify @Nullable のような @Target(ElementType.TYPE_USE) で定義されたアノテーションは、最後の . の後に内部型または完全修飾型で指定する必要があることも規定されています。

  • Cache.@Nullable ValueWrapper

  • jakarta.validation.@Nullable Validator

一般的な使用例では、@NonNull (英語) @NullUnmarked (英語) が必要になることはほとんどありません。

Spring Framework の Null 可能性と JSR-305 アノテーション

Spring Framework の nullability アノテーションを使用すると、リポジトリメソッドの null 可能性制約を表現できます。

Spring Framework 7 と同様に、Spring の null 可能性アノテーションは JSpecify に置き換えられて非推奨になりました。詳細については、Spring の null 安全アノテーションから JSpecify への移行のフレームワークドキュメントを参照してください。

これらは、ツールフレンドリーなアプローチを提供し、次のように実行時に 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 可能性を宣言する

null 以外のデフォルトが設定されると、リポジトリクエリメソッドの呼び出しは、実行時に null 可能性の制約について検証されます。クエリ結果が定義された制約に違反している場合、例外がスローされます。これは、メソッドが null を返すが、null 許容ではないと宣言されている場合に発生します(リポジトリが存在するパッケージで定義されたアノテーションのデフォルト)。null 許容の結果に再度オプトインする場合は、個々のメソッドで @Nullable を選択的に使用します。このセクションの冒頭で説明した結果ラッパー型を使用すると、引き続き期待どおりに機能します。空の結果は、不在を表す値に変換されます。

次の例は、今説明したいくつかの手法を示しています。

さまざまな null 値制約を使用する
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 リポジトリは、言語メカニズムを使用してこれらの制約を定義し、次のように同じランタイムチェックを適用します。

Kotlin リポジトリでの null 可能性制約の使用
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 を返します。