クエリメソッド

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

例 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: クエリメソッドでサポートされるキーワード
キーワード サンプル 論理的な結果

And

findByLastnameAndFirstname

… WHERE lastname = :lastname AND firstname = :firstname

Or

findByLastnameOrFirstname

… WHERE lastname = :lastname OR firstname = :firstname

Is, Equals

findByFirstname,findByFirstnameIs,findByFirstnameEquals

… WHERE firstname = :firstname (or … WHERE firstname IS NULL if the argument is null)

Between

findByStartDateBetween

… WHERE startDate BETWEEN :startDate and :startDate1

LessThan

findByAgeLessThan

… WHERE age < :age

LessThanEqual

findByAgeLessThanEqual

… WHERE age <= :age

GreaterThan

findByAgeGreaterThan

… WHERE age > :age

GreaterThanEqual

findByAgeGreaterThanEqual

… WHERE age >= :age

After

findByStartDateAfter

… WHERE startDate > :startDate

Before

findByStartDateBefore

… WHERE startDate < :startDate

IsNull, Null

findByAge(Is)Null

… WHERE age IS NULL

IsNotNull, NotNull

findByAge(Is)NotNull

… WHERE age IS NOT NULL

Like

findByFirstnameLike

… WHERE firstname LIKE :firstname

NotLike

findByFirstnameNotLike

… WHERE firstname NOT LIKE :firstname

StartingWith

findByFirstnameStartingWith

… WHERE firstname LIKE :firstname (parameter bound with appended %)

EndingWith

findByFirstnameEndingWith

… WHERE firstname LIKE :firstname (parameter bound with prepended %)

Containing

findByFirstnameContaining

… WHERE firstname LIKE :firstname (parameter bound wrapped in %)

OrderBy

findByAgeOrderByLastnameDesc

… WHERE age = :age ORDER BY lastname DESC

Not

findByLastnameNot

… WHERE lastname <> :lastname (or … WHERE lastname IS NOT NULL if the argument is null)

In

findByAgeIn(Collection<Age> ages)

… WHERE age IN (:ages)

NotIn

findByAgeNotIn(Collection<Age> ages)

… WHERE age NOT IN (:ages)

True

findByActiveTrue()

… WHERE active = :active (with active being TRUE)

False

findByActiveFalse()

… WHERE active = :active (with active being FALSE)

IgnoreCase

findByFirstnameIgnoreCase

… where UPPER(firstname) = UPPER(:firstname)

クエリの変更

前のセクションでは、特定のエンティティまたはエンティティのコレクションにアクセスするためのクエリを宣言する方法について説明しました。前の表のキーワードを使用すると、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 式は 2 つの方法で使用できます。

SpEL 式は、クエリを実行する直前に評価される述語値を提供できます。

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

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

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

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

Expression を使用するもう 1 つの方法は、パラメーターに関係なく、クエリの途中で使用することです。クエリを評価した結果によって、クエリ文字列内の式が置き換えられます。

クエリで SpEL を使用する
@Query("SELECT * FROM #{#tableName} WHERE lastname = :lastname")
Flux<Person> findByQueryWithExpression(String lastname);

これは最初の実行前に一度評価され、2 つの変数 tableName と qualifiedTableName が追加された StandardEvaluationContext を使用します。この使用箇所は、テーブル名自体が動的な場合に最も便利です。テーブル名自体が SpEL 式も使用するためです。

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