例示による問い合わせ
導入
この章では、Query by Example の概要とその使用方法について説明します。
Query by Example(QBE)は、シンプルなインターフェースを備えた使いやすいクエリ手法です。動的なクエリの作成が可能になり、フィールド名を含むクエリを作成する必要がなくなります。実際、Query by Example では、ストア固有のクエリ言語を使用してクエリを記述する必要はまったくありません。
この章では、Query by Example の中心的な概念について説明します。情報は Spring Data Commons モジュールから取得されます。データベースによっては、文字列一致のサポートが制限される場合があります。 |
使用方法
例示による問い合わせ API は、次の 4 つの部分で構成されています。
プローブ: フィールドが設定されたドメインオブジェクトの実際の例。
ExampleMatcher
:ExampleMatcher
には、特定のフィールドの照合方法に関する詳細が記載されています。複数の例で再利用できます。Example
:Example
は、プローブとExampleMatcher
で構成されています。クエリの作成に使用されます。FetchableFluentQuery
:FetchableFluentQuery
は、Example
から派生したクエリをさらにカスタマイズできる流れるような API を提供します。Fluent API を使用すると、クエリの順序付け射影と結果処理を指定できます。
例示による問い合わせは、いくつかのユースケースに適しています。
静的または動的な制約のセットを使用してデータストアをクエリします。
既存のクエリを壊すことを心配せずにドメインオブジェクトを頻繁にリファクタリングします。
基盤となるデータストア API から独立して動作します。
例示による問い合わせには、いくつかの制限もあります。
firstname = ?0 or (firstname = ?1 and lastname = ?2)
など、ネストまたはグループ化されたプロパティ制約はサポートされていません。文字列マッチングにおけるストア固有のサポート。データベースによっては、文字列一致では文字列の開始 / 含む / 終了 / 正規表現をサポートできます。
他のプロパティ型と完全に一致します。
Query by Example を開始する前に、ドメインオブジェクトが必要です。開始するには、次の例に示すように、リポジトリのインターフェースを作成します。
public class Person {
@Id
private String id;
private String firstname;
private String lastname;
private Address address;
// … getters and setters omitted
}
前の例は、単純なドメインオブジェクトを示しています。これを使用して Example
を作成できます。デフォルトでは、null
値を持つフィールドは無視され、文字列はストア固有のデフォルトを使用して照合されます。
例示による問い合わせ条件へのプロパティの包含は、null 可能性に基づいています。プリミティブ型(int 、double 、…)を使用するプロパティは、ExampleMatcher はプロパティパスを無視しますでない限り、常に含まれます。 |
例は、of
ファクトリメソッドを使用するか、ExampleMatcher
を使用して作成できます。Example
は不変です。次のリストは、簡単な例を示しています。
Person person = new Person(); (1)
person.setFirstname("Dave"); (2)
Example<Person> example = Example.of(person); (3)
1 | ドメインオブジェクトの新しいインスタンスを作成します。 |
2 | クエリにプロパティを設定します。 |
3 | Example を作成します。 |
リポジトリを使用して、サンプルクエリを実行できます。これを行うには、リポジトリインターフェースに QueryByExampleExecutor<T>
を継承させます。次のリストは、QueryByExampleExecutor
インターフェースからの抜粋を示しています。
QueryByExampleExecutor
public interface QueryByExampleExecutor<T> {
<S extends T> S findOne(Example<S> example);
<S extends T> Iterable<S> findAll(Example<S> example);
// … more functionality omitted.
}
マッチャーの例
例はデフォルト設定に限定されません。次の例に示すように、ExampleMatcher
を使用して、文字列照合、null 処理、プロパティ固有の設定に独自のデフォルトを指定できます。
Person person = new Person(); (1)
person.setFirstname("Dave"); (2)
ExampleMatcher matcher = ExampleMatcher.matching() (3)
.withIgnorePaths("lastname") (4)
.withIncludeNullValues() (5)
.withStringMatcher(StringMatcher.ENDING); (6)
Example<Person> example = Example.of(person, matcher); (7)
1 | ドメインオブジェクトの新しいインスタンスを作成します。 |
2 | セットのプロパティ。 |
3 | ExampleMatcher を作成して、すべての値が一致することを期待します。この段階では、さらに構成しなくても使用できます。 |
4 | lastname プロパティパスを無視する新しい ExampleMatcher を構築します。 |
5 | 新しい ExampleMatcher を作成して、lastname プロパティパスを無視し、null 値を含めます。 |
6 | 新しい ExampleMatcher を作成して、lastname プロパティパスを無視し、null 値を含め、サフィックス文字列の照合を実行します。 |
7 | ドメインオブジェクトと設定された ExampleMatcher に基づいて新しい Example を作成します。 |
デフォルトでは、ExampleMatcher
はプローブに設定されたすべての値が一致することを期待しています。暗黙的に定義された述語のいずれかに一致する結果を取得する場合は、ExampleMatcher.matchingAny()
を使用します。
個々のプロパティ(「名」や「姓」、ネストされたプロパティの場合は "address.city" など)の動作を指定できます。次の例に示すように、一致するオプションと大文字と小文字の区別を使用して調整できます。
ExampleMatcher matcher = ExampleMatcher.matching()
.withMatcher("firstname", endsWith())
.withMatcher("lastname", startsWith().ignoreCase());
}
マッチャーオプションを構成する別の方法は、ラムダ(Java 8 で導入)を使用することです。このアプローチは、実装者にマッチャーの変更を要求するコールバックを作成します。設定オプションはマッチャーインスタンス内に保持されているため、マッチャーを返す必要はありません。次の例は、ラムダを使用するマッチャーを示しています。
ExampleMatcher matcher = ExampleMatcher.matching()
.withMatcher("firstname", match -> match.endsWith())
.withMatcher("firstname", match -> match.startsWith());
}
Example
によって作成されたクエリは、構成の統合ビューを使用します。デフォルトのマッチング設定は ExampleMatcher
レベルで設定できますが、個々の設定は特定のプロパティパスに適用できます。ExampleMatcher
で設定された設定は、明示的に定義されていない限り、プロパティパス設定に継承されます。プロパティパッチの設定は、デフォルト設定よりも優先されます。次の表は、さまざまな ExampleMatcher
設定の範囲を説明しています。
設定 | スコープ |
---|---|
null ハンドリング |
|
文字列マッチング |
|
プロパティを無視する | プロパティパス |
大文字と小文字の区別 |
|
値変換 | プロパティパス |
Fluent API
QueryByExampleExecutor
は、これまでメンションしなかったもう 1 つの方法、<S extends T, R> R findBy(Example<S> example, Function<FluentQuery.FetchableFluentQuery<S>, R> queryFunction)
を提供します。他のメソッドと同様に、Example
から派生したクエリを実行します。ただし、2 番目の引数を使用すると、他の方法では動的に制御できない実行の側面を制御できます。これを行うには、2 番目の引数で FetchableFluentQuery
のさまざまなメソッドを呼び出します。sortBy
を使用すると、結果の順序を指定できます。as
では、結果を変換する型を指定できます。project
は、照会される属性を制限します。first
、firstValue
、one
、oneValue
、all
、page
、stream
、count
、exists
は、取得する結果の種類と、予想よりも多くの結果が利用可能な場合のクエリの動作を定義します。
Optional<Person> match = repository.findBy(example,
q -> q
.sortBy(Sort.by("lastname").descending())
.first()
);
例の実行
次の例では、リポジトリに対して例示による問い合わせを使用しています。
interface PersonRepository extends ListQueryByExampleExecutor<Person> {
}
class PersonService {
@Autowired PersonRepository personRepository;
List<Person> findPeople(Person probe) {
return personRepository.findAll(Example.of(probe));
}
}
Redis リポジトリは、セカンダリインデックスとともに、Spring Data の Query byExample 機能のサブセットをサポートします。特に、クエリの作成には、正確で大文字と小文字が区別され、null 以外の値のみが使用されます。
セカンダリインデックスは、セットベースの操作(セット交差、セットユニオン)を使用して、一致するキーを決定します。インデックスが存在しないクエリにプロパティを追加しても、インデックスが存在しないため、結果は返されません。例示による問い合わせのサポートは、インデックス構成をインスペクションして、インデックスでカバーされるプロパティのみをクエリに含めます。これは、インデックス付けされていないプロパティが誤って含まれるのを防ぐためです。
大文字と小文字を区別しないクエリとサポートされていない StringMatcher
インスタンスは、実行時に拒否されます。
次のリストは、サポートされている例示による問い合わせオプションを示しています。
単純なプロパティとネストされたプロパティの大文字と小文字を区別した完全一致
任意 / すべての一致モード
条件値の値変換
条件からの
null
値の除外
次のリストは、Query byExample でサポートされていないプロパティを示しています。
大文字と小文字を区別しないマッチング
正規表現、プレフィックス / 含む / サフィックス文字列照合
関連付け、コレクション、マップのようなプロパティのクエリ
条件からの
null
値の包含ソート付き
findAll