web-dev-qa-db-ja.com

Spring Data RESTのQueryDSL統合を使用して、より複雑なクエリを実行できますか?

現在、REST APIを構築しています。このAPIを使用すると、特定のエンティティのほとんどのプロパティを簡単にフィルタリングできます。 QueryDSLSpringデータREST例:Oliver Gierke )を使用すると、プロパティを参照するクエリパラメーター(例:/users?firstName=Dennis&lastName=Laumen)。

QuerydslBinderCustomizerインターフェイスを実装することで、クエリパラメーターとエンティティのプロパティ間のマッピングをカスタマイズすることもできます(たとえば、大文字と小文字を区別しない検索や部分的な文字列の一致など)。これはすべて素晴らしいですが、クライアントが範囲を使用していくつかのタイプをフィルタリングできるようにしたいと思っています。たとえば、生年月日のようなプロパティに関して、次のようなことをしたいと思います、/users?dateOfBirthFrom=1981-1-1&dateOfBirthTo=1981-12-31。同じことが数値ベースのプロパティ/users?idFrom=100&idTo=200。これはQuerydslBinderCustomizerインターフェイスを使用して可能であると感じていますが、これらの2つのライブラリ間の統合についてはあまり詳しく説明されていません。

結論、これはSpring Data RESTとQueryDSLを使用して可能ですか?そうであれば、どのようにですか?

29
Dennis Laumen

次のカスタマイズを使用してこれを機能させることができるはずです。

_bindings.bind(user.dateOfBirth).all((path, value) -> {

  Iterator<? extends LocalDate> it = value.iterator();
  return path.between(it.next(), it.next());
});
_

ここで重要なのは、_?dateOfBirth=…&dateOfBirth=_(プロパティを2回使用する)と….all(…)バインディングを使用して、提供されたすべての値にアクセスできるようにすることです。

SpringがdateOfBirthUser- propertyに_@DateTimeFormat_アノテーションを追加して、Springが受信したStringsLocalDateインスタンスに正しく変換できることを確認してください。

ラムダは現在、_Collection<? extends T>_を取得しています。これにより、個々の要素のもつれを解消するのが少し難しくなりますが、将来のリリースでは、Listを公開するようにこれを変更できると思います。

28
Oliver Drotbohm

コメントに投稿されたので、フィールド名creationDateFromcreationDateToに応じて異なる動作をする必要もありました。それを機能させるために、私は次のことをしました:

最初に、_@QueryEntity_アノテーションと2つのフィールドをエンティティクラスに追加しました。フィールドには次の注釈が付けられました。

  • _@Transient_したがって、フィールドは永続化されません
  • @Getter(value = AccessLevel.PRIVATE) Lombokを使用しているため、注釈によりフィールドが応答本文から非表示になります
  • @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)は、urlクエリパラメータの日付を解析するための形式を処理します

_@QueryEntity
@Entity
public class MyEntity implements Serializable {
  ...

  @Column(updatable = false)
  @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
  private Date creationDate;

  @Transient
  @Getter(value = AccessLevel.PRIVATE)
  @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
  private Date creationDateTo;

  @Transient
  @Getter(value = AccessLevel.PRIVATE)
  @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
  private Date creationDateFrom;

  ...
}  
_

次に、querydslクラスの生成方法をJPAAnnotationProcessorからQuerydslAnnotationProcessorに変更しました。このようにして、_@Transient_で注釈が付けられたフィールドは、引き続きQMyEntityで生成されますが、永続化されません。 pomのプラグイン設定:

_<plugin>
    <groupId>com.mysema.maven</groupId>
    <artifactId>apt-maven-plugin</artifactId>
    <version>1.1.3</version>
    <executions>
        <execution>
            <phase>generate-sources</phase>
            <goals>
                <goal>process</goal>
            </goals>
            <configuration>
                <outputDirectory>target/generated-sources/annotations</outputDirectory>
                <processor>com.querydsl.apt.QuerydslAnnotationProcessor</processor>
            </configuration>
        </execution>
    </executions>
</plugin>
_

最後に、QuerydslBinderCustomizerを拡張し、creationDateFromおよびcreationDateToに関連するバインディングをカスタマイズしましたが、creationDateに適切なロジックを適用しました

_@Override
default void customize(QuerydslBindings bindings, QMyEntity root) {
    bindings.bind(root.creationDateFrom).first((path, value) -> 
                                                root.creationDate.after(value));
    bindings.bind(root.creationDateTo).first((path, value) ->
                                               root.creationDate.before(value));
}
_

これらすべてを使用して、基準のいずれか、両方、またはまったく使用せずに日付範囲クエリを実行できます。

_http://localhost:8080/myentities?creation_date_to=2017-05-08
http://localhost:8080/myentities?creation_date_from=2017-01-01
http://localhost:8080/myentities?creation_date_from=2017-01-01&creation_date_to=2017-05-08
_
12
Jorge C

これは、すべての日付フィールドの汎用バインディングに使用したものであり、常に2つの値、fromおよびtoを期待しています。

_bindings.bind(Date.class).all((final DateTimePath<Date> path, final Collection<? extends Date> values) -> {
    final List<? extends Date> dates = new ArrayList<>(values);
    Collections.sort(dates);
    if (dates.size() == 2) {
        return path.between(dates.get(0), dates.get(1));
    }
    throw new IllegalArgumentException("2 date params(from & to) expected for:" + path + " found:" + values);
});
_

これは日時フィールド用です。日付フィールドの場合、単一のパラメーターを取得する場合、path.eq()は意味があると思います。

5
gazal