クエリメソッド
このセクションでは、Spring Data JDBC の実装と使用に関する特定の情報を提供します。
通常、リポジトリでトリガーするデータアクセス操作のほとんどは、データベースに対して実行されるクエリになります。このようなクエリの定義は、次の例に示すように、リポジトリインターフェースでメソッドを宣言することです。
interface PersonRepository extends PagingAndSortingRepository<Person, String> {
List<Person> findByFirstname(String firstname); (1)
List<Person> findByFirstnameOrderByLastname(String firstname, Pageable pageable); (2)
Slice<Person> findByLastname(String lastname, Pageable pageable); (3)
Page<Person> findByLastname(String lastname, Pageable pageable); (4)
Person findByFirstnameAndLastname(String firstname, String lastname); (5)
Person findFirstByLastname(String lastname); (6)
@Query("SELECT * FROM person WHERE lastname = :lastname")
List<Person> findByLastname(String lastname); (7)
@Query("SELECT * FROM person WHERE lastname = :lastname")
Stream<Person> streamByLastname(String lastname); (8)
@Query("SELECT * FROM person WHERE username = :#{ principal?.username }")
Person findActiveUser(); (9)
}
1 | このメソッドは、指定された firstname を持つすべての人々のクエリを示します。クエリは、And および Or と連結できる制約のメソッド名を解析することによって導出されます。メソッド名は SELECT … FROM person WHERE firstname = :firstname のクエリ式になります。 |
2 | Pageable を使用して、オフセットと並べ替えのパラメーターをデータベースに渡します。 |
3 | Slice<Person> を返します。LIMIT+1 行を選択して、さらに消費するデータがあるかどうかを判断します。ResultSetExtractor のカスタマイズはサポートされていません。 |
4 | Page<Person> を返すページ分割されたクエリを実行します。指定されたページ境界内のデータのみを選択し、場合によっては合計数を決定するためのカウントクエリを選択します。ResultSetExtractor カスタマイズはサポートされていません。 |
5 | 指定された条件で単一のエンティティを検索します。一意でない結果の場合は、IncorrectResultSizeDataAccessException で完了します。 |
6 | <3> とは対照的に、クエリがより多くの結果ドキュメントを生成した場合でも、最初のエンティティは常に出力されます。 |
7 | findByLastname メソッドは、指定された lastname を持つすべての人のクエリを表示します。 |
8 | streamByLastname メソッドは Stream を返します。これにより、データベースから値が返されるとすぐに値が可能になります。 |
9 | Spring 式言語を使用して、パラメーターを動的に解決できます。サンプルでは、現在のユーザーのユーザー名を解決するために Spring Security が使用されています。 |
次の表は、クエリメソッドでサポートされるキーワードを示しています。
キーワード | サンプル | 論理的な結果 |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
クエリの派生は、結合を使用せずに WHERE 句で使用できるプロパティに制限されています。 |
クエリ検索戦略
JDBC モジュールは、@Query
アノテーションの文字列またはプロパティファイルの名前付きクエリとしてクエリを手動で定義することをサポートします。
メソッドの名前からクエリを取得することは、現在、単純なプロパティに制限されています。つまり、プロパティは集約ルートに直接存在します。また、このアプローチでは、select クエリのみがサポートされています。
@Query
を使用する
次の例は、@Query
を使用してクエリメソッドを宣言する方法を示しています。
interface UserRepository extends CrudRepository<User, Long> {
@Query("select firstName, lastName from User u where u.emailAddress = :email")
User findByEmailAddress(@Param("email") String email);
}
クエリ結果をエンティティに変換する場合、デフォルトでは、Spring Data JDBC が生成するクエリと同じ RowMapper
が使用されます。指定するクエリは、RowMapper
が予期する形式と一致する必要があります。エンティティのコンストラクターで使用されるすべてのプロパティの列を提供する必要があります。setter を介して設定されるプロパティの列、ウィザーまたはフィールドアクセスはオプションです。結果に一致する列がないプロパティは設定されません。クエリは、集約ルート、埋め込みエンティティ、SQL 配列型として保存およびロードされるプリミティブ型の配列を含む 1 対 1 の関連を設定するために使用されます。エンティティのマップ、リスト、セット、配列に対して個別のクエリが生成されます。
プロパティ 1 対 1 の関連の名前には、関連の名前と _
のプレフィックスを付ける必要があります。たとえば、上記の例の User
にプロパティ city
を持つ address
がある場合、その city
の列には address_city
というラベルを付ける必要があります。
文字列ベースのクエリは、ページネーションをサポートしておらず、クエリパラメーターとして Sort 、PageRequest 、Limit を受け入れないことに注意してください。これらのクエリの場合、クエリを書き直す必要があるからです。制限を適用したい場合は、SQL を使用してこの意図を表現し、適切なパラメーターをクエリにバインドしてください。 |
クエリには SpEL 式を含めることができます。評価方法が異なる 2 つのバリアントがあります。
最初のバリアントでは、SpEL 式に :
というプレフィックスが付けられ、バインド変数のように使用されます。このような SpEL 式はバインド変数に置き換えられ、変数は SpEL 式の結果にバインドされます。
@Query("SELECT * FROM person WHERE id = :#{#person.id}")
Person findWithSpEL(PersonRef person);
上の例で示したように、これを使用してパラメーターのメンバーにアクセスできます。より複雑なユースケースでは、EvaluationContextExtension
をアプリケーションコンテキストで使用できるようにすることができ、これにより、SpEL で任意のオブジェクトを使用できるようになります。
他のバリアントはクエリ内のどこでも使用でき、クエリを評価した結果によってクエリ文字列内の式が置き換えられます。
@Query("SELECT * FROM #{tableName} WHERE id = :id")
Person findWithSpEL(PersonRef person);
これは最初の実行前に一度評価され、2 つの変数 tableName
と qualifiedTableName
が追加された StandardEvaluationContext
を使用します。この使用箇所は、テーブル名自体が動的な場合に最も便利です。テーブル名自体が SpEL 式も使用するためです。
Spring は、-parameters コンパイラーフラグに基づく Java 8 のパラメーター名の検出を完全にサポートしています。デバッグ情報の代替としてビルドでこのフラグを使用することにより、名前付きパラメーターの @Param アノテーションを省略できます。 |
Spring Data JDBC は、名前付きパラメーターのみをサポートします。 |
名前付きクエリ
前のセクションで説明したように、アノテーションにクエリが指定されていない場合、Spring Data JDBC は名前付きクエリを見つけようとします。クエリの名前を決定する方法は 2 つあります。デフォルトでは、クエリのドメインクラス、つまりリポジトリの集約ルートを取得し、その単純名を取得して、.
で区切られたメソッドの名前を追加します。あるいは、@Query
アノテーションには、検索するクエリの名前を指定するために使用できる name
属性があります。
名前付きクエリは、クラスパスのプロパティファイル META-INF/jdbc-named-queries.properties
で提供されることが期待されています。
そのファイルの場所は、値を @EnableJdbcRepositories.namedQueriesLocation
に設定することにより変更できます。
名前付きクエリは、アノテーションによって提供されるクエリと同じ方法で処理されます。
ストリーミング結果
クエリメソッドの戻り値の型として Stream を指定すると、Spring Data JDBC は要素が使用可能になるとすぐに要素を返します。大量のデータを処理する場合、これは遅延とメモリ要件を削減するのに適しています。
ストリームには、データベースへのオープン接続が含まれています。メモリリークを回避するには、ストリームを閉じて、最終的にその接続を閉じる必要があります。そのための推奨される方法は try-with-resource clause
です。また、データベースへの接続が閉じられると、ストリームはそれ以上の要素を取得できず、例外をスローする可能性があることも意味します。
カスタム RowMapper
または ResultSetExtractor
@Query
アノテーションを使用すると、使用するカスタム RowMapper
または ResultSetExtractor
を指定できます。属性 rowMapperClass
および resultSetExtractorClass
を使用すると、使用するクラスを指定できます。これらのクラスは、デフォルトのコンストラクターを使用してインスタンス化されます。あるいは、Spring アプリケーションコンテキストから rowMapperClassRef
または resultSetExtractorClassRef
を Bean 名に設定することもできます。
単一のメソッドだけでなく、特定の型を返すカスタムクエリを持つすべてのメソッドに対して特定の RowMapper
を使用する場合は、メソッドの戻り値の型ごとに RowMapperMap
Bean を登録し、RowMapper
を登録できます。次の例は、DefaultQueryMappingConfiguration
を登録する方法を示しています。
@Bean
QueryMappingConfiguration rowMappers() {
return new DefaultQueryMappingConfiguration()
.register(Person.class, new PersonRowMapper())
.register(Address.class, new AddressRowMapper());
}
メソッドに使用する RowMapper
を決定するときは、メソッドの戻り値の型に基づいて、次の手順に従います。
型が単純型の場合、
RowMapper
は使用されません。代わりに、クエリは単一の列を持つ単一の行を返すことが期待され、戻り値の型への変換がその値に適用されます。
QueryMappingConfiguration
のエンティティクラスは、問題の戻り値型のスーパークラスまたはインターフェースであるものが見つかるまで繰り返されます。そのクラスに登録されたRowMapper
が使用されます。反復は登録順に行われるため、特定の型の後に、より一般的な型を登録するようにしてください。
該当する場合、コレクションや Optional
などのラッパー型はアンラップされます。戻り値の型 Optional<Person>
は、前のプロセスで Person
型を使用します。
カスタム RowMapper から QueryMappingConfiguration 、@Query(rowMapperClass= …) 、カスタム ResultSetExtractor を使用すると、結果マッピングが必要に応じて独自のイベント / コールバックを発行できるため、エンティティのコールバックとライフサイクルイベントが無効になります。 |
変更クエリ
次の例に示すように、query メソッドで @Modifying
を使用して、クエリを変更クエリとしてマークできます。
@Modifying
@Query("UPDATE DUMMYENTITY SET name = :name WHERE id = :id")
boolean updateName(@Param("id") Long id, @Param("name") String name);
以下の戻り値の型を指定できます。
void
int
(更新されたレコード数)boolean
(レコードが更新されたかどうか)
変更クエリは、データベースに対して直接実行されます。イベントやコールバックは呼び出されません。アノテーション付きクエリで更新されない場合、監査アノテーションを含むフィールドも更新されません。