最新の安定バージョンについては、Spring Data Commons 3.5.5 を使用してください! |
Spring Data 拡張
このセクションでは、さまざまなコンテキストで Spring Data を使用できるようにする一連の Spring Data 拡張について説明します。現在、ほとんどの統合は Spring MVC を対象としています。
Querydsl 拡張
Querydsl (英語) は、流れるような API を使用して、静的に型指定された SQL のようなクエリの構築を可能にするフレームワークです。
次の例に示すように、いくつかの Spring Data モジュールは、QuerydslPredicateExecutor を介して Querydsl との統合を提供します。
public interface QuerydslPredicateExecutor<T> {
Optional<T> findById(Predicate predicate); (1)
Iterable<T> findAll(Predicate predicate); (2)
long count(Predicate predicate); (3)
boolean exists(Predicate predicate); (4)
// … more functionality omitted.
}| 1 | Predicate に一致する単一のエンティティを検索して返します。 |
| 2 | Predicate に一致するすべてのエンティティを検索して返します。 |
| 3 | Predicate に一致するエンティティの数を返します。 |
| 4 | Predicate に一致するエンティティが存在するかどうかを返します。 |
Querydsl サポートを使用するには、次の例に示すように、リポジトリインターフェースで QuerydslPredicateExecutor を継承します。
interface UserRepository extends CrudRepository<User, Long>, QuerydslPredicateExecutor<User> {
} 前の例では、次の例に示すように、Querydsl Predicate インスタンスを使用して型安全なクエリを記述できます。
Predicate predicate = user.firstname.equalsIgnoreCase("dave")
.and(user.lastname.startsWithIgnoreCase("mathews"));
userRepository.findAll(predicate);Web サポート
リポジトリプログラミングモデルをサポートする Spring Data モジュールには、さまざまな Web サポートが付属しています。Web 関連のコンポーネントでは、Spring MVC JAR がクラスパス上にある必要があります。それらのいくつかは、Spring HATEOAS [GitHub] (英語) との統合さえ提供します。一般に、統合サポートは、次の例に示すように、JavaConfig 構成クラスで @EnableSpringDataWebSupport アノテーションを使用することで有効になります。
Java
XML
@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
class WebConfiguration {}
<bean class="org.springframework.data.web.config.SpringDataWebConfiguration" />
<!-- If you use Spring HATEOAS, register this one *instead* of the former -->
<bean class="org.springframework.data.web.config.HateoasAwareSpringDataWebConfiguration" />
@EnableSpringDataWebSupport アノテーションは、いくつかのコンポーネントを登録します。これらについては、このセクションの後半で説明します。また、クラスパスで Spring HATEOAS を検出し、統合コンポーネント(存在する場合)も登録します。
基本的な Web サポート
前のセクションで示した構成は、いくつかの基本的なコンポーネントを登録します。
Spring MVC がリクエストパラメーターまたはパス変数からリポジトリ管理ドメインクラスのインスタンスを解決できるようにする
DomainClassConverterクラスの使用。Spring MVC がリクエストパラメーターから
PageableおよびSortインスタンスを解決できるようにするHandlerMethodArgumentResolver実装。Jackson モジュールは、使用する Spring Data モジュールに応じて、
PointやDistanceなどの型を逆 / 直列化するか、特定の型を格納します。
DomainClassConverter クラスの使用
DomainClassConverter クラスを使用すると、Spring MVC コントローラーメソッドシグネチャーでドメイン型を直接使用できるため、次の例に示すように、リポジトリからインスタンスを手動で検索する必要がありません。
@Controller
@RequestMapping("/users")
class UserController {
@RequestMapping("/{id}")
String showUserForm(@PathVariable("id") User user, Model model) {
model.addAttribute("user", user);
return "userForm";
}
} このメソッドは User インスタンスを直接受け取り、それ以上のルックアップは必要ありません。インスタンスは、Spring MVC が最初にパス変数をドメインクラスの id 型に変換し、最終的にドメイン型に登録されたリポジトリインスタンスで findById(…) を呼び出してインスタンスにアクセスすることで解決できます。
現在、リポジトリは変換のために発見される資格があるために CrudRepository を実装しなければなりません。 |
ページング可能およびソート用の HandlerMethodArgumentResolvers
前のセクションで示した構成スニペットは、PageableHandlerMethodArgumentResolver と SortHandlerMethodArgumentResolver のインスタンスも登録します。次の例に示すように、登録により、Pageable および Sort が有効なコントローラーメソッド引数として有効になります。
@Controller
@RequestMapping("/users")
class UserController {
private final UserRepository repository;
UserController(UserRepository repository) {
this.repository = repository;
}
@RequestMapping
String showUsers(Model model, Pageable pageable) {
model.addAttribute("users", repository.findAll(pageable));
return "users";
}
} 上記のメソッドシグネチャーにより、Spring MVC は、次のデフォルト構成を使用して、リクエストパラメーターから Pageable インスタンスを派生させようとします。
| 取得するページ。0 からインデックス付けされ、デフォルトは 0 です。 |
| 取得するページのサイズ。デフォルトは 20 です。 |
|
|
この動作をカスタマイズするには、PageableHandlerMethodArgumentResolverCustomizer インターフェースまたは SortHandlerMethodArgumentResolverCustomizer インターフェースをそれぞれ実装する Bean を登録します。次の例に示すように、customize() メソッドが呼び出され、設定を変更できます。
@Bean SortHandlerMethodArgumentResolverCustomizer sortCustomizer() {
return s -> s.setPropertyDelimiter("<-->");
} 既存の MethodArgumentResolver のプロパティを設定するだけでは目的に合わない場合は、SpringDataWebConfiguration または HATEOAS 対応の拡張機能を継承し、pageableResolver() または sortResolver() メソッドをオーバーライドし、@Enable アノテーションを使用する代わりにカスタマイズした構成ファイルをインポートします。
リクエストから複数の Pageable または Sort インスタンスを解決する必要がある場合(たとえば、複数のテーブルの場合)、Spring の @Qualifier アノテーションを使用して互いに区別できます。次に、リクエストパラメーターの前に ${qualifier}_ を付ける必要があります。次の例は、結果のメソッドシグネチャーを示しています。
String showUsers(Model model,
@Qualifier("thing1") Pageable first,
@Qualifier("thing2") Pageable second) { … }thing1_page、thing2_page などを設定する必要があります。
メソッドに渡されるデフォルトの Pageable は PageRequest.of(0, 20) と同等ですが、Pageable パラメーターの @PageableDefault アノテーションを使用してカスタマイズできます。
Page および Slice のハイパーメディアサポート
Spring HATEOAS には、Page または Slice インスタンスのコンテンツを必要な Page/Slice メタデータとリンクで強化して、クライアントがページを簡単にナビゲートできるようにする表現モデルクラス (PagedModel/SlicedModel) が付属しています。Page から PagedModel への変換は、PagedResourcesAssembler と呼ばれる Spring HATEOAS RepresentationModelAssembler インターフェースの実装によって行われます。同様に、Slice インスタンスは SlicedResourcesAssembler を使用して SlicedModel に変換できます。次の例は、SlicedResourcesAssembler がまったく同じように機能するため、PagedResourcesAssembler をコントローラーメソッドの引数として使用する方法を示しています。
@Controller
class PersonController {
private final PersonRepository repository;
// Constructor omitted
@GetMapping("/people")
HttpEntity<PagedModel<Person>> people(Pageable pageable,
PagedResourcesAssembler assembler) {
Page<Person> people = repository.findAll(pageable);
return ResponseEntity.ok(assembler.toModel(people));
}
} 前の例に示すように、構成を有効にすると、PagedResourcesAssembler をコントローラーメソッドの引数として使用できます。その上で toModel(…) を呼び出すと、次の効果があります。
Pageのコンテンツは、PagedModelインスタンスのコンテンツになります。PagedModelオブジェクトはPageMetadataインスタンスをアタッチし、Pageおよび基礎となるPageableからの情報が取り込まれます。PagedModelには、ページの状態に応じて、prevおよびnextリンクが添付される場合があります。リンクは、メソッドがマップする URI を指します。メソッドに追加されたページネーションパラメーターは、PageableHandlerMethodArgumentResolverの設定と一致して、リンクを後で解決できるようにします。
データベースに 30 個の Person インスタンスがあると仮定します。これで、リクエスト(GET localhost:8080/people)をトリガーして、次のような出力を確認できます。
{ "links" : [
{ "rel" : "next", "href" : "http://localhost:8080/persons?page=1&size=20" }
],
"content" : [
… // 20 Person instances rendered here
],
"pageMetadata" : {
"size" : 20,
"totalElements" : 30,
"totalPages" : 2,
"number" : 0
}
} ここに示されている JSON エンベロープ形式は、正式に指定された構造に従っておらず、安定しているとは保証されておらず、いつでも変更される可能性があります。HAL などの Spring HATEOAS でサポートされている、ハイパーメディア対応の公式メディア型としてレンダリングを有効にすることを強くお勧めします。これらは、@EnableHypermediaSupport アノテーションを使用してアクティブ化できます。詳細については、Spring HATEOAS リファレンスドキュメントを参照してください。 |
アセンブラーは正しい URI を生成し、デフォルト構成を選択して、パラメーターを次のリクエストの Pageable に解決しました。つまり、その構成を変更すると、リンクは自動的に変更に準拠します。デフォルトでは、アセンブラーはそれが呼び出されたコントローラーメソッドを指しますが、ページネーションリンクを構築するためのベースとして使用されるカスタム Link を渡すことにより、それをカスタマイズできます。これにより、PagedResourcesAssembler.toModel(…) メソッドがオーバーロードされます。
Spring Data Jackson モジュール
コアモジュール、および一部のストア固有のモジュールには、Spring Data ドメインで使用される org.springframework.data.geo.Distance や org.springframework.data.geo.Point などの型の Jackson モジュールのセットが付属しています。
これらのモジュールは、Web サポートが有効になり、com.fasterxml.jackson.databind.ObjectMapper が使用可能になるとインポートされます。
初期化中に、SpringDataJacksonConfiguration と同様に SpringDataJacksonModules がインフラストラクチャによって取得されるため、宣言された com.fasterxml.jackson.databind.Module が Jackson ObjectMapper で使用できるようになります。
次のドメイン型のデータバインディングミックスインは、共通のインフラストラクチャによって登録されます。
org.springframework.data.geo.Distance org.springframework.data.geo.Point org.springframework.data.geo.Box org.springframework.data.geo.Circle org.springframework.data.geo.Polygon
個々のモジュールは、追加の |
Web データバインディングのサポート
次の例に示すように、Spring Data 射影(射影で説明)を使用して、JSONPath (英語) 式(Jayway JsonPath [GitHub] (英語) が必要)または XPath [W3C] (英語) 式(XmlBeam (英語) が必要)のいずれかを使用して、受信リクエストペイロードをバインドできます。
@ProjectedPayload
public interface UserPayload {
@XBRead("//firstname")
@JsonPath("$..firstname")
String getFirstname();
@XBRead("/lastname")
@JsonPath({ "$.lastname", "$.user.lastname" })
String getLastname();
} 前の例に示されている型は、Spring MVC ハンドラーメソッドの引数として使用するか、RestTemplate のいずれかのメソッドで ParameterizedTypeReference を使用することで使用できます。上記のメソッド宣言は、指定されたドキュメント内の任意の場所で firstname を見つけようとします。lastname XML ルックアップは、受信ドキュメントのトップレベルで実行されます。の JSON バリアントは、トップレベルの lastname を最初に試行しますが、前者が値を返さない場合は、user サブドキュメントにネストされた lastname も試行します。こうすることで、クライアントが公開メソッドを呼び出さなくても、ソースドキュメントの構造の変更を簡単に軽減できます (通常、クラスベースのペイロードバインディングの欠点です)。
ネストされた射影は、射影に従ってサポートされます。メソッドがインターフェース以外の複雑な型を返す場合、Jackson ObjectMapper が最終値のマッピングに使用されます。
Spring MVC の場合、@EnableSpringDataWebSupport がアクティブになり、必要な依存関係がクラスパスで使用可能になるとすぐに、必要なコンバーターが自動的に登録されます。RestTemplate で使用する場合は、手動で ProjectingJackson2HttpMessageConverter (JSON) または XmlBeamHttpMessageConverter を登録します。
詳細については、標準の Spring Data サンプルリポジトリ [GitHub] (英語) の Web 射影の例 [GitHub] (英語) を参照してください。
Querydsl Web サポート
QueryDSL (英語) が統合されているストアの場合、Request クエリ文字列に含まれている属性からクエリを派生させることができます。
次のクエリ文字列を検討してください。
?firstname=Dave&lastname=Matthews 前の例の User オブジェクトが与えられた場合、次のように QuerydslPredicateArgumentResolver を使用して、クエリ文字列を次の値に解決できます。
QUser.user.firstname.eq("Dave").and(QUser.user.lastname.eq("Matthews"))Querydsl がクラスパスで見つかると、この機能は @EnableSpringDataWebSupport とともに自動的に有効になります。 |
メソッドシグネチャーに @QuerydslPredicate を追加すると、すぐに使用できる Predicate が提供されます。これは、QuerydslPredicateExecutor を使用して実行できます。
型情報は通常、メソッドの戻り値型から解決されます。その情報は必ずしもドメイン型と一致しないため、QuerydslPredicate の root 属性を使用することをお勧めします。 |
次の例は、メソッドシグネチャーで @QuerydslPredicate を使用する方法を示しています。
@Controller
class UserController {
@Autowired UserRepository repository;
@RequestMapping(value = "/", method = RequestMethod.GET)
String index(Model model, @QuerydslPredicate(root = User.class) Predicate predicate, (1)
Pageable pageable, @RequestParam MultiValueMap<String, String> parameters) {
model.addAttribute("users", repository.findAll(predicate, pageable));
return "index";
}
}| 1 | User の一致する Predicate にクエリ文字列引数を解決します。 |
デフォルトのバインディングは次のとおりです。
eqとしての単純なプロパティのObject。containsのようなプロパティのようなコレクションのObject。inとしての単純なプロパティのCollection。
これらのバインディングは、@QuerydslPredicate の bindings 属性を使用するか、Java 8 default methods を使用して、次のようにリポジトリインターフェースに QuerydslBinderCustomizer メソッドを追加することによってカスタマイズできます。
interface UserRepository extends CrudRepository<User, String>,
QuerydslPredicateExecutor<User>, (1)
QuerydslBinderCustomizer<QUser> { (2)
@Override
default void customize(QuerydslBindings bindings, QUser user) {
bindings.bind(user.username).first((path, value) -> path.contains(value)) (3)
bindings.bind(String.class)
.first((StringPath path, String value) -> path.containsIgnoreCase(value)); (4)
bindings.excluding(user.password); (5)
}
}| 1 | QuerydslPredicateExecutor は、Predicate の特定のファインダーメソッドへのアクセスを提供します。 |
| 2 | リポジトリインターフェースで定義された QuerydslBinderCustomizer が自動的に選択され、ショートカット @QuerydslPredicate(bindings=…) が選択されます。 |
| 3 | username プロパティのバインディングを単純な contains バインディングとして定義します。 |
| 4 | String プロパティのデフォルトのバインディングを、大文字と小文字を区別しない contains 一致になるように定義します。 |
| 5 | password プロパティを Predicate 解決から除外します。 |
リポジトリまたは @QuerydslPredicate から特定のバインディングを適用する前に、デフォルトの Querydsl バインディングを保持する QuerydslBinderCustomizerDefaults Bean を登録できます。 |
リポジトリポピュレーター
Spring JDBC モジュールを使用している場合は、おそらく DataSource に SQL スクリプトを取り込むためのサポートに精通しているでしょう。同様の抽象化がリポジトリレベルで利用できますが、ストアに依存しない必要があるため、データ定義言語として SQL を使用しません。ポピュレーターは XML(Spring の OXM 抽象化による)と JSON(Jackson による)をサポートして、リポジトリにデータを取り込むデータを定義します。
次の内容の data.json というファイルがあるとします。
[ { "_class" : "com.acme.Person",
"firstname" : "Dave",
"lastname" : "Matthews" },
{ "_class" : "com.acme.Person",
"firstname" : "Carter",
"lastname" : "Beauford" } ]Spring Data Commons で提供されるリポジトリ名前空間の populator 要素を使用して、リポジトリにデータを取り込むことができます。上記のデータを PersonRepository に入力するには、次のようなポピュレーターを宣言します。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:repository="http://www.springframework.org/schema/data/repository"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/repository
https://www.springframework.org/schema/data/repository/spring-repository.xsd">
<repository:jackson2-populator locations="classpath:data.json" />
</beans> 上記の宣言により、data.json ファイルは Jackson ObjectMapper によって読み取られ、逆直列化されます。
JSON オブジェクトが非整列化される型は、JSON ドキュメントの _class 属性を調べることで決定されます。インフラストラクチャは最終的に、適切なリポジトリを選択して、デシリアライズされたオブジェクトを処理します。
代わりに、XML を使用してリポジトリにデータを取り込む必要のあるデータを定義するには、unmarshaller-populator エレメントを使用できます。Spring OXM で使用可能な XML マーシャラーオプションの 1 つを使用するように構成します。詳細については、Spring リファレンスドキュメントを参照してください。次の例は、JAXB を使用してリポジトリポピュレータをアンマーシャルする方法を示しています。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:repository="http://www.springframework.org/schema/data/repository"
xmlns:oxm="http://www.springframework.org/schema/oxm"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/repository
https://www.springframework.org/schema/data/repository/spring-repository.xsd
http://www.springframework.org/schema/oxm
https://www.springframework.org/schema/oxm/spring-oxm.xsd">
<repository:unmarshaller-populator locations="classpath:data.json"
unmarshaller-ref="unmarshaller" />
<oxm:jaxb2-marshaller contextPath="com.acme" />
</beans>