クエリメソッド

通常、リポジトリでトリガーするデータアクセス操作のほとんどは、データベースに対して実行されるクエリになります。このようなクエリの定義は、次の例に示すように、リポジトリインターフェースでメソッドを宣言することです。

例 1: PersonRepository とクエリメソッド
interface ReactivePersonRepository extends ReactiveSortingRepository<Person, Long> {

  Flux<Person> findByFirstname(String firstname);                                   (1)

  Flux<Person> findByFirstname(Publisher<String> firstname);                        (2)

  Flux<Person> findByFirstnameOrderByLastname(String firstname, Pageable pageable); (3)

  Mono<Person> findByFirstnameAndLastname(String firstname, String lastname);       (4)

  Mono<Person> findFirstByLastname(String lastname);                                (5)

  @Query("SELECT * FROM person WHERE lastname = :lastname")
  Flux<Person> findByLastname(String lastname);                                     (6)

  @Query("SELECT firstname, lastname FROM person WHERE lastname = $1")
  Mono<Person> findFirstByLastname(String lastname);                                (7)
}
1 このメソッドは、指定された firstname を持つすべての人々のクエリを示します。クエリは、And および Or と連結できる制約のメソッド名を解析することによって導出されます。メソッド名は SELECT … FROM person WHERE firstname = :firstname のクエリ式になります。
2 このメソッドは、指定された Publisher によって firstname が発行されると、指定された firstname を持つすべての人々のクエリを示します。
3Pageable を使用して、オフセットと並べ替えのパラメーターをデータベースに渡します。
4 指定された条件で単一のエンティティを検索します。一意でない結果の場合は、IncorrectResultSizeDataAccessException で完了します。
5<4> でない限り、クエリでより多くの結果行が生成された場合でも、最初のエンティティは常に発行されます。
6findByLastname メソッドは、指定された姓を持つすべての人々のクエリを表示します。
7firstname および lastname 列のみを射影する単一の Person エンティティの照会。アノテーション付きクエリは、この例では Postgres バインドマーカーであるネイティブバインドマーカーを使用します。

@Query アノテーションで使用される select ステートメントの列は、それぞれのプロパティに対して NamingStrategy によって生成された名前と一致する必要があることに注意してください。select ステートメントに一致する列が含まれていない場合、そのプロパティは設定されません。そのプロパティが永続性コンストラクターで必要な場合は、null または(プリミティブ型の場合)デフォルト値が提供されます。

次の表は、クエリメソッドでサポートされるキーワードを示しています。

表 1: クエリメソッドでサポートされるキーワード
キーワード サンプル 論理的な結果

After

findByBirthdateAfter(Date date)

birthdate > date

GreaterThan

findByAgeGreaterThan(int age)

age > age

GreaterThanEqual

findByAgeGreaterThanEqual(int age)

age >= age

Before

findByBirthdateBefore(Date date)

birthdate < date

LessThan

findByAgeLessThan(int age)

age < age

LessThanEqual

findByAgeLessThanEqual(int age)

age <= age

Between

findByAgeBetween(int from, int to)

age BETWEEN from AND to

NotBetween

findByAgeNotBetween(int from, int to)

age NOT BETWEEN from AND to

In

findByAgeIn(Collection<Integer> ages)

age IN (age1, age2, ageN)

NotIn

findByAgeNotIn(Collection ages)

age NOT IN (age1, age2, ageN)

IsNotNull, NotNull

findByFirstnameNotNull()

firstname IS NOT NULL

IsNull, Null

findByFirstnameNull()

firstname IS NULL

Like, StartingWith, EndingWith

findByFirstnameLike(String name)

firstname LIKE name

NotLike, IsNotLike

findByFirstnameNotLike(String name)

firstname NOT LIKE name

Containing on String

findByFirstnameContaining(String name)

firstname LIKE '%' + name +'%'

NotContaining on String

findByFirstnameNotContaining(String name)

firstname NOT LIKE '%' + name +'%'

(No keyword)

findByFirstname(String name)

firstname = name

Not

findByFirstnameNot(String name)

firstname != name

IsTrue, True

findByActiveIsTrue()

active IS TRUE

IsFalse, False

findByActiveIsFalse()

active IS FALSE

クエリの変更

前のセクションでは、特定のエンティティまたはエンティティのコレクションにアクセスするためのクエリを宣言する方法について説明しました。前の表のキーワードを使用すると、delete … By または remove … By と組み合わせて、一致する行を削除する派生クエリを作成できます。

例 2: Delete … By クエリ
interface ReactivePersonRepository extends ReactiveSortingRepository<Person, String> {

  Mono<Integer> deleteByLastname(String lastname);            (1)

  Mono<Void> deletePersonByLastname(String lastname);         (2)

  Mono<Boolean> deletePersonByLastname(String lastname);      (3)
}
1Mono<Integer> の戻り型を使用すると、影響を受ける行の数が返されます。
2Void を使用すると、結果値を出力せずに行が正常に削除されたかどうかがレポートされるだけです。
3Boolean を使用すると、少なくとも 1 つの行が削除されたかどうかが報告されます。

このアプローチは包括的なカスタム機能に適しているため、次の例に示すように、クエリメソッドに @Modifying アノテーションを付けることで、パラメーターバインディングのみが必要なクエリを変更できます。

@Modifying
@Query("UPDATE person SET firstname = :firstname where lastname = :lastname")
Mono<Integer> setFixedFirstnameFor(String firstname, String lastname);

変更クエリの結果は次のとおりです。

  • Void (または Kotlin Unit)は、更新カウントを破棄して完了を待ちます。

  • 影響を受ける行数を出力する Integer または別の数値型。

  • 少なくとも 1 つの行が更新されたかどうかを出力する Boolean

@Modifying アノテーションは、@Query アノテーションと組み合わせた場合にのみ関連します。派生したカスタムメソッドでは、このアノテーションは不要です。

変更クエリは、データベースに対して直接実行されます。イベントやコールバックは呼び出されません。アノテーション付きクエリで更新されない場合、監査アノテーションを含むフィールドも更新されません。

または、Spring Data リポジトリのカスタム実装で説明されている機能を使用して、カスタム変更動作を追加できます。

@Query を使用する

次の例は、@Query を使用してクエリメソッドを宣言する方法を示しています。

@Query を使用してクエリメソッドを宣言する
interface UserRepository extends ReactiveCrudRepository<User, Long> {

  @Query("select firstName, lastName from User u where u.emailAddress = :email")
  Flux<User> findByEmailAddress(@Param("email") String email);
}
文字列ベースのクエリは、ページネーションをサポートしておらず、クエリパラメーターとして SortPageRequestLimit を受け入れないことに注意してください。これらのクエリの場合、クエリを書き直す必要があるからです。制限を適用したい場合は、SQL を使用してこの意図を表現し、適切なパラメーターをクエリにバインドしてください。
Spring は、-parameters コンパイラーフラグに基づく Java 8 のパラメーター名の検出を完全にサポートしています。デバッグ情報の代替としてビルドでこのフラグを使用することにより、名前付きパラメーターの @Param アノテーションを省略できます。

SpEL 式を使用したクエリ

クエリ文字列定義を SpEL 式と一緒に使用して、実行時に動的クエリを作成できます。SpEL 式は、クエリを実行する直前に評価される述語値を提供できます。

式は、すべての引数を含む配列を通じてメソッド引数を公開します。次のクエリは、[0] を使用して、lastname の述語値を宣言します(これは、:lastname パラメーターバインディングと同等です)。

@Query("SELECT * FROM person WHERE lastname = :#{[0]}")
Flux<Person> findByQueryWithExpression(String lastname);

クエリ文字列の SpEL は、クエリを強化する強力な方法です。しかし、彼らはまた、不要な引数の広い範囲を受け入れることができます。クエリに不要な変更が加えられないように、クエリに渡す前に文字列をサニタイズしてください。

式のサポートは、Query SPI: org.springframework.data.spel.spi.EvaluationContextExtension を介して拡張可能です。Query SPI は、プロパティと機能を提供し、ルートオブジェクトをカスタマイズできます。拡張機能は、クエリの作成時の SpEL 評価時にアプリケーションコンテキストから取得されます。

SpEL 式をプレーンパラメーターと組み合わせて使用する場合は、ネイティブバインドマーカーの代わりに名前付きパラメーター表記を使用して、適切なバインド順序を確保してください。