© 2018-2019 The original authors.

このドキュメントのコピーは、印刷物または電子的に配布されるかどうかにかかわらず、各コピーにこの著作権表示が含まれていることを条件に、ユーザー自身の使用および他者への配布のために作成できます。

序文

Spring Data JDBCは、JDBCに基づくリポジトリ抽象化を提供します。

プロジェクトメタデータ

1. 注目の新機能

このセクションでは、各バージョンの重要な変更について説明します。

1.1. Spring Data JDBC 1.1の新機能

  • @Embedded エンティティのサポート。

  • byte[]BINARYとして保存します。

  • JdbcAggregateTemplateの専用 insert メソッド。

  • 読み取り専用のプロパティのサポート。

1.2. Spring Data JDBC 1.0の新機能

  • CrudRepositoryの基本的なサポート。

  • @Query サポート。

  • MyBatisサポート。

  • ID生成。

  • イベントのサポート。

  • 監査。

  • CustomConversions .

2. 依存関係

個々のSpring Dataモジュールの開始日が異なるため、それらのほとんどには異なるメジャーバージョン番号とマイナーバージョン番号が付いています。互換性のあるものを見つける最も簡単な方法は、定義された互換性のあるバージョンとともに提供されるSpring Data Release Train BOMに依存することです。Mavenプロジェクトでは、次のように、POMの <dependencyManagement /> セクションでこの依存関係を宣言します。

例 1: Spring DataリリーストレインBOMの使用
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-releasetrain</artifactId>
      <version>Moore-SR4</version>
      <scope>import</scope>
      <type>pom</type>
    </dependency>
  </dependencies>
</dependencyManagement>

現在のリリーストレインバージョンは Moore-SR4です。列車名はアルファベット順に昇順になり、現在利用可能な列車がここにリストされます(GitHub) 。バージョン名は次のパターンに従います: ${name}-${release}、ここでreleaseは以下のいずれかです。

  • BUILD-SNAPSHOT : 現在のスナップショット

  • M1, M2など: マイルストーン

  • RC1, RC2など: リリース候補

  • RELEASE : GAリリース

  • SR1, SR2など: サービスリリース

BOMの使用例は、Spring Dataサンプルリポジトリ(GitHub) にあります。これを配置すると、次のように、<dependencies /> ブロックにバージョンなしで使用するSpring Dataモジュールを宣言できます。

例 2: Spring Dataモジュールへの依存関係の宣言
<dependencies>
  <dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jpa</artifactId>
  </dependency>
<dependencies>

2.1. Spring Bootを使用した依存関係管理

Spring Bootは、Spring Dataモジュールの最新バージョンを選択します。それでも新しいバージョンにアップグレードする場合は、プロパティ spring-data-releasetrain.version を使用するトレイン名とイテレーションに設定します。

2.2. Spring Framework

現行バージョンのSpring Dataモジュールには、バージョン5.2.3.RELEASE以上のSpring Frameworkが必要です。モジュールは、マイナーバージョンの古いバグ修正バージョンでも動作する場合があります。ただし、その世代内の最新バージョンを使用することを強くお勧めします。

3. Spring Dataリポジトリの操作

Spring Dataリポジトリの抽象化のゴールは、さまざまな永続ストアのデータアクセスレイヤーを実装するために必要な定型コードの量を大幅に削減することです。

Spring Dataリポジトリのドキュメントとモジュール

この章では、Spring Dataリポジトリのコア概念とインターフェースについて説明します。この章の情報は、Spring Data Commonsモジュールから引用されています。Java Persistence API(JPA)モジュールの構成とコードサンプルを使用します。XML名前空間宣言と型を、使用する特定のモジュールに相当するものに拡張する必要があります。「名前空間リファレンス」は、リポジトリAPIをサポートするすべてのSpring DataモジュールでサポートされるXML構成をカバーしています。「リポジトリクエリキーワード」は、一般的なリポジトリ抽象化でサポートされるクエリメソッドキーワードをカバーしています。モジュールの特定の機能の詳細については、このドキュメントのそのモジュールに関する章を参照してください。

3.1. コアコンセプト

Spring Dataリポジトリ抽象化の中心的なインターフェースは Repositoryです。ドメインクラスと、ドメインクラスのIDタイプを型引数として管理します。このインターフェースは、主にマーカーインターフェースとして機能し、使用するタイプをキャプチャーし、このインターフェースを継承するインターフェースを発見できます。 CrudRepository は、管理されているエンティティクラスに洗練されたCRUD機能を提供します。

例 3: CrudRepository インターフェース
public interface CrudRepository<T, ID> extends Repository<T, ID> {

  <S extends T> S save(S entity);      (1)

  Optional<T> findById(ID primaryKey); (2)

  Iterable<T> findAll();               (3)

  long count();                        (4)

  void delete(T entity);               (5)

  boolean existsById(ID primaryKey);   (6)

  // … more functionality omitted.
}
1指定されたエンティティを保存します。
2指定されたIDで識別されるエンティティを返します。
3すべてのエンティティを返します。
4エンティティの数を返します。
5指定されたエンティティを削除します。
6指定されたIDのエンティティが存在するかどうかを示します。
また、JpaRepositoryMongoRepositoryなどの永続化技術固有の抽象化も提供します。これらのインターフェースは CrudRepository を継承し、CrudRepositoryなどのかなり汎用的な永続化テクノロジーにとらわれないインターフェースに加えて、基礎となる永続化テクノロジーの機能を公開します。

CrudRepositoryの上部には、エンティティへのページ分割されたアクセスを容易にするための追加のメソッドを追加する PagingAndSortingRepository 抽象化があります。

例 4: PagingAndSortingRepository インターフェース
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {

  Iterable<T> findAll(Sort sort);

  Page<T> findAll(Pageable pageable);
}

20のページサイズで User の2番目のページにアクセスするには、次のようなことができます。

PagingAndSortingRepository<User, Long> repository = // … get access to a bean
Page<User> users = repository.findAll(PageRequest.of(1, 20));

クエリメソッドに加えて、カウントクエリと削除クエリの両方のクエリ派生を使用できます。次のリストは、派生カウントクエリのインターフェース定義を示しています。

例 5: 派生カウントクエリ
interface UserRepository extends CrudRepository<User, Long> {

  long countByLastname(String lastname);
}

次のリストは、派生削除クエリのインターフェース定義を示しています。

例 6: 派生削除クエリ
interface UserRepository extends CrudRepository<User, Long> {

  long deleteByLastname(String lastname);

  List<User> removeByLastname(String lastname);
}

3.2. クエリメソッド

通常、標準のCRUD機能リポジトリには、基になるデータストアに対するクエリがあります。Spring Dataでは、これらのクエリを宣言することは4ステップのプロセスになります。

  1. 次の例に示すように、リポジトリまたはそのサブインターフェースの1つを継承するインターフェースを宣言し、処理するドメインクラスとIDタイプに入力します。

    interface PersonRepository extends Repository<Person, Long> { … }
  2. インターフェースでクエリメソッドを宣言します。

    interface PersonRepository extends Repository<Person, Long> {
      List<Person> findByLastname(String lastname);
    }
  3. JavaConfigまたはXML 構成を使用して、Springをセットアップして、これらのインターフェースのプロキシインスタンスを作成します。

    1. Java構成を使用するには、次のようなクラスを作成します。

      import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
      
      @EnableJpaRepositories
      class Config { … }
    2. XML構成を使用するには、次のようなBeanを定義します。

      <?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:jpa="http://www.springframework.org/schema/data/jpa"
         xsi:schemaLocation="http://www.springframework.org/schema/beans
           https://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/data/jpa
           https://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
      
         <jpa:repositories base-package="com.acme.repositories"/>
      
      </beans>

    この例では、JPA名前空間が使用されています。リポジトリの抽象化を他のストアに使用する場合、これをストアモジュールの適切なネームスペース宣言に変更する必要があります。つまり、たとえば mongodbを優先して jpa を交換する必要があります。

    +また、アノテーション付きクラスのパッケージがデフォルトで使用されるため、JavaConfigバリアントはパッケージを明示的に構成しないことに注意してください。スキャンするパッケージをカスタマイズするには、データストア固有のリポジトリの @Enable${store}Repositories -annotationの basePackage… 属性のいずれかを使用します。

  4. 次の例に示すように、リポジトリインスタンスを挿入して使用します。

    class SomeClient {
    
      private final PersonRepository repository;
    
      SomeClient(PersonRepository repository) {
        this.repository = repository;
      }
    
      void doSomething() {
        List<Person> persons = repository.findByLastname("Matthews");
      }
    }

以下のセクションでは、各ステップについて詳しく説明します。

3.3. リポジトリインターフェースの定義

最初に、ドメインクラス固有のリポジトリインターフェースを定義します。インターフェースは Repository を継承し、ドメインクラスとIDの型を指定する必要があります。そのドメインタイプのCRUDメソッドを公開する場合は、Repositoryではなく CrudRepository を継承します。

3.3.1. リポジトリ定義の微調整

通常、リポジトリインターフェースは Repository, CrudRepositoryまたは PagingAndSortingRepositoryを継承します。あるいは、Spring Dataインターフェースを継承したくない場合は、@RepositoryDefinitionを使用してリポジトリインターフェースにアノテーションを付けることもできます。 CrudRepository を継承すると、エンティティを操作するためのメソッドの完全なセットが公開されます。公開するメソッドを選択する場合、CrudRepository から公開するメソッドをドメインリポジトリにコピーします。

そうすることで、提供されたSpring Dataリポジトリ機能の上に独自の抽象化を定義できます。

次の例は、CRUDメソッド(この場合はfindById および save)を選択的に公開する方法を示しています。

例 7: CRUDメソッドを選択的に公開する
@NoRepositoryBean
interface MyBaseRepository<T, ID> extends Repository<T, ID> {

  Optional<T> findById(ID id);

  <S extends T> S save(S entity);
}

interface UserRepository extends MyBaseRepository<User, Long> {
  User findByEmailAddress(EmailAddress emailAddress);
}

前の例では、すべてのドメインリポジトリと公開された findById(…) および save(…)に共通のベースインターフェースを定義しました。これらのメソッドは、Spring Dataが提供する選択のストアのベースリポジトリ実装にルーティングされます(たとえば、JPAを使用する場合、実装は SimpleJpaRepositoryです)。これは、それらが CrudRepositoryのメソッドシグネチャーと一致するためです。そのため、UserRepository はユーザーを保存し、IDで個々のユーザーを検索し、メールアドレスで Users を検索するクエリをトリガーできるようになりました。

中間リポジトリインターフェースには @NoRepositoryBeanのアノテーションが付けられています。Spring Dataが実行時にインスタンスを作成してはならないすべてのリポジトリインターフェースに、そのアノテーションを必ず追加してください。

3.3.2. 複数のSpring Dataモジュールでリポジトリを使用する

定義済みスコープ内のすべてのリポジトリインターフェースがSpring Dataモジュールにバインドされているため、アプリケーションで一意のSpring Dataモジュールを使用すると、物事が簡単になります。アプリケーションによっては、複数のSpring Dataモジュールを使用する必要がある場合があります。そのような場合、リポジトリ定義は永続化テクノロジーを区別する必要があります。クラスパスで複数のリポジトリファクトリを検出すると、Spring Dataは厳密なリポジトリ構成モードに入ります。厳密な構成では、リポジトリまたはドメインクラスの詳細を使用して、リポジトリ定義のSpring Dataモジュールバインディングについて決定します。

  1. リポジトリ定義がモジュール固有のリポジトリを継承する場合、特定のSpring Dataモジュールの有効な候補です。

  2. ドメインクラスにモジュール固有のタイプアノテーションが付けられている場合、特定のSpring Dataモジュールの有効な候補です。Spring Dataモジュールは、サードパーティのアノテーション(JPAの @Entityなど)を受け入れるか、独自のアノテーション(Spring Data MongoDBおよびSpring Data Elasticsearchの @Document など)を提供します。

次の例は、モジュール固有のインターフェース(この場合はJPA)を使用するリポジトリを示しています。

例 8: モジュール固有のインターフェースを使用したリポジトリ定義
interface MyRepository extends JpaRepository<User, Long> { }

@NoRepositoryBean
interface MyBaseRepository<T, ID> extends JpaRepository<T, ID> { … }

interface UserRepository extends MyBaseRepository<User, Long> { … }

MyRepository および UserRepository は、型階層で JpaRepository を継承します。それらは、Spring Data JPAモジュールの有効な候補です。

次の例は、汎用インターフェースを使用するリポジトリを示しています。

例 9: ジェネリックインターフェースを使用したリポジトリ定義
interface AmbiguousRepository extends Repository<User, Long> { … }

@NoRepositoryBean
interface MyBaseRepository<T, ID> extends CrudRepository<T, ID> { … }

interface AmbiguousUserRepository extends MyBaseRepository<User, Long> { … }

AmbiguousRepository および AmbiguousUserRepository は、それらの型階層で Repository および CrudRepository のみを継承します。一意のSpring Dataモジュールを使用する場合、これはまったく問題ありませんが、複数のモジュールは、これらのリポジトリをバインドする特定のSpring Dataを区別できません。

次の例は、アノテーション付きのドメインクラスを使用するリポジトリを示しています。

例 10: アノテーション付きのドメインクラスを使用したリポジトリ定義
interface PersonRepository extends Repository<Person, Long> { … }

@Entity
class Person { … }

interface UserRepository extends Repository<User, Long> { … }

@Document
class User { … }

PersonRepository は、JPA @Entity アノテーションが付けられた Personを参照するため、このリポジトリは明らかにSpring Data JPAに属します。 UserRepository は、Spring Data MongoDBの @Document アノテーションが付けられた Userを参照します。

次の悪い例は、アノテーションが混在するドメインクラスを使用するリポジトリを示しています。

例 11: アノテーションが混在するドメインクラスを使用したリポジトリ定義
interface JpaPersonRepository extends Repository<Person, Long> { … }

interface MongoDBPersonRepository extends Repository<Person, Long> { … }

@Entity
@Document
class Person { … }

この例は、JPAとSpring Dataの両方のMongoDBアノテーションを使用したドメインクラスを示しています。 JpaPersonRepositoryMongoDBPersonRepositoryの2つのリポジトリを定義します。1つはJPA向けで、もう1つはMongoDBの使用向けです。Spring Dataはリポジトリを区別することができなくなり、未定義の動作につながります。

リポジトリタイプの詳細および識別ドメインクラスアノテーションは、特定のSpring Dataモジュールのリポジトリ候補を識別するための厳密なリポジトリ構成に使用されます。同じドメインタイプで複数の永続化テクノロジ固有のアノテーションを使用することが可能であり、複数の永続化テクノロジでドメインタイプを再利用できます。ただし、Spring Dataは、リポジトリをバインドする一意のモジュールを決定できなくなります。

リポジトリを区別する最後の方法は、リポジトリベースパッケージをスコープすることです。ベースパッケージは、リポジトリインターフェース定義のスキャンの開始点を定義します。これは、適切なパッケージにリポジトリ定義があることを意味します。デフォルトでは、アノテーション駆動型の構成では、構成クラスのパッケージが使用されます。XMLベースの構成の基本パッケージは必須です。

次の例は、基本パッケージのアノテーション駆動型の構成を示しています。

例 12: 基本パッケージのアノテーション駆動型構成
@EnableJpaRepositories(basePackages = "com.acme.repositories.jpa")
@EnableMongoRepositories(basePackages = "com.acme.repositories.mongo")
class Configuration { … }

3.4. クエリメソッドの定義

リポジトリプロキシには、メソッド名からストア固有のクエリを派生させる2つの方法があります。

  • メソッド名から直接クエリを導出します。

  • 手動で定義されたクエリを使用します。

利用可能なオプションは、実際のストアによって異なります。ただし、作成する実際のクエリを決定する戦略が必要です。次のセクションでは、使用可能なオプションについて説明します。

3.4.1. クエリ検索戦略

クエリを解決するリポジトリインフラストラクチャでは、次の戦略を使用できます。XML構成では、query-lookup-strategy 属性を使用して名前空間で戦略を構成できます。Java構成の場合、Enable${store}Repositories アノテーションの queryLookupStrategy 属性を使用できます。特定のデータストアでは一部の戦略がサポートされていない場合があります。

  • CREATE は、クエリメソッド名からストア固有のクエリを作成しようとします。一般的なアプローチは、メソッド名から既知のプレフィックスの特定のセットを削除し、メソッドの残りを解析することです。クエリ構築の詳細については、「クエリ作成」を参照してください。

  • USE_DECLARED_QUERY は宣言されたクエリを見つけようとし、見つからない場合は例外をスローします。クエリは、アノテーションによってどこかで定義するか、他の手段で宣言できます。特定のストアのドキュメントを参照して、そのストアで利用可能なオプションを見つけてください。リポジトリインフラストラクチャが、ブートストラップ時にメソッドの宣言されたクエリを見つけられない場合、失敗します。

  • CREATE_IF_NOT_FOUND (デフォルト)は、CREATEUSE_DECLARED_QUERYを組み合わせたものです。最初に宣言されたクエリを検索し、宣言されたクエリが見つからない場合は、カスタムメソッド名ベースのクエリを作成します。これはデフォルトのルックアップ戦略であるため、明示的に何も設定しない場合に使用されます。メソッド名による迅速なクエリ定義だけでなく、必要に応じて宣言されたクエリを導入することにより、これらのクエリのカスタムチューニングも可能です。

3.4.2. クエリ作成

Spring Dataリポジトリインフラストラクチャに組み込まれたクエリビルダメカニズムは、リポジトリのエンティティに対して制約クエリを構築できます。このメカニズムは、メソッドからプレフィックス find…By, read…By, query…By, count…Byおよび get…By を取り除き、残りの解析を開始します。導入句には、作成するクエリに個別のフラグを設定する Distinct などの追加の式を含めることができます。ただし、最初の By は区切り文字として機能し、実際の基準の開始を示します。非常に基本的なレベルでは、エンティティプロパティの条件を定義し、And および Orで連結できます。次の例は、いくつかのクエリを作成する方法を示しています。

例 13: メソッド名からのクエリ作成
interface PersonRepository extends Repository<Person, Long> {

  List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);

  // Enables the distinct flag for the query
  List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
  List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);

  // Enabling ignoring case for an individual property
  List<Person> findByLastnameIgnoreCase(String lastname);
  // Enabling ignoring case for all suitable properties
  List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);

  // Enabling static ORDER BY for a query
  List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
  List<Person> findByLastnameOrderByFirstnameDesc(String lastname);
}

メソッドの解析の実際の結果は、クエリを作成する永続ストアによって異なります。ただし、注意すべき一般的な事項がいくつかあります。

  • 式は通常、連結可能な演算子と組み合わせたプロパティトラバーサルです。プロパティ式を AND および ORと組み合わせることができます。また、プロパティ式の Between, LessThan, GreaterThanLike などの演算子のサポートも取得できます。サポートされる演算子はデータストアによって異なる場合があるため、リファレンスドキュメントの適切な部分を参照してください。

  • メソッドパーサーは、個々のプロパティ(たとえば findByLastnameIgnoreCase(…))または大文字と小文字の区別をサポートするタイプのすべてのプロパティ(通常は String インスタンス- findByLastnameAndFirstnameAllIgnoreCase(…)など)の IgnoreCase フラグの設定をサポートします。ケースの無視がサポートされているかどうかはストアによって異なるため、ストア固有のクエリメソッドについては、リファレンスドキュメントの関連セクションを参照してください。

  • プロパティを参照するクエリメソッドに OrderBy 句を追加し、並べ替え方向(Asc または Desc)を提供することにより、静的な順序を適用できます。動的な並べ替えをサポートするクエリメソッドを作成するには、「特別なパラメーター処理」を参照してください。

3.4.3. プロパティ式

前の例に示すように、プロパティ式は管理対象エンティティの直接プロパティのみを参照できます。クエリの作成時に、解析されたプロパティが管理対象ドメインクラスのプロパティであることを既に確認しています。ただし、ネストされたプロパティを走査して制約を定義することもできます。次のメソッドシグネチャーを検討してください。

List<Person> findByAddressZipCode(ZipCode zipCode);

PersonAddress があり、ZipCodeがあるとします。その場合、メソッドはプロパティトラバーサル x.address.zipCodeを作成します。解決アルゴリズムは、パーツ全体(AddressZipCode)をプロパティとして解釈することから始まり、その名前(大文字ではない)のプロパティのドメインクラスをチェックします。アルゴリズムが成功した場合、そのプロパティを使用します。そうでない場合、アルゴリズムはキャメルケース部分のソースを右側から頭と尾に分割し、対応するプロパティ(この例では AddressZipCode)を見つけようとします。アルゴリズムがそのヘッドを持つプロパティを見つけると、テールを取得し、そこからツリーを構築し続け、先ほど説明したメソッドでテールを分割します。最初の分割が一致しない場合、アルゴリズムは分割ポイントを左(Address, ZipCode)に移動して続行します。

これはほとんどの場合に機能するはずですが、アルゴリズムが間違ったプロパティを選択する可能性があります。 Person クラスにも addressZip プロパティがあるとします。アルゴリズムは最初の分割ラウンドですでに一致し、間違ったプロパティを選択して失敗します( addressZip のタイプにはおそらく code プロパティがないため)。

このあいまいさを解決するには、メソッド名内で _ を使用して、トラバーサルポイントを手動で定義します。メソッド名は次のようになります。

List<Person> findByAddress_ZipCode(ZipCode zipCode);

アンダースコア文字を予約文字として扱うため、標準のJava命名規則に従うことを強くお勧めします(つまり、プロパティ名にアンダースコアを使用せず、代わりにキャメルケースを使用します)。

3.4.4. 特別なパラメーター処理

クエリでパラメータを処理するには、前の例で既に見たようにメソッドパラメータを定義します。それに加えて、インフラストラクチャは PageableSortなどの特定の特定のタイプを認識し、ページネーションとソートをクエリに動的に適用します。次の例は、これらの機能を示しています。

例 14: 照会メソッドで Pageable, Sliceおよび Sort を使用する
Page<User> findByLastname(String lastname, Pageable pageable);

Slice<User> findByLastname(String lastname, Pageable pageable);

List<User> findByLastname(String lastname, Sort sort);

List<User> findByLastname(String lastname, Pageable pageable);
Sort および Pageable を使用するAPIは、null 以外の値がメソッドに渡されることを想定しています。並べ替えやページネーションを適用したくない場合は、Sort.unsorted()Pageable.unpaged()を使用してください。

最初の方法では、org.springframework.data.domain.Pageable インスタンスをクエリメソッドに渡して、静的に定義されたクエリにページングを動的に追加できます。 Page は、利用可能な要素とページの総数を知っています。これは、インフラストラクチャがカウントクエリをトリガーして、全体の数を計算することによって行われます。これは(使用するストアによっては)高価になる可能性があるため、代わりに Sliceを返すことができます。 Slice は、次の Slice が使用可能かどうかのみを認識します。これは、より大きな結果セットをウォークスルーするときに十分な場合があります。

ソートオプションも Pageable インスタンスを介して処理されます。ソートのみが必要な場合は、org.springframework.data.domain.Sort パラメーターをメソッドに追加します。ご覧のとおり、List を返すことも可能です。この場合、実際の Page インスタンスを構築するために必要な追加のメタデータは作成されません(つまり、必要な追加のカウントクエリが発行されないことを意味します)。むしろ、特定の範囲のエンティティのみを検索するようにクエリを制限します。

クエリ全体で取得するページ数を調べるには、追加のカウントクエリをトリガーする必要があります。デフォルトでは、このクエリは実際にトリガーするクエリから派生します。
ページングとソート

単純なソート式は、プロパティ名を使用して定義できます。式を連結して、複数の条件を1つの式にまとめることができます。

例 15: ソート式の定義
Sort sort = Sort.by("firstname").ascending()
  .and(Sort.by("lastname").descending());

タイプセーフなソート式の定義方法については、タイプからソート式を定義し、メソッド参照を使用してソートするプロパティを定義します。

例 16: タイプセーフAPIを使用してソート式を定義する
TypedSort<Person> person = Sort.sort(Person.class);

TypedSort<Person> sort = person.by(Person::getFirstname).ascending()
  .and(person.by(Person::getLastname).descending());

ストアの実装がQuerydslをサポートしている場合、生成されたメタモデルタイプを使用してソート式を定義することもできます。

例 17: Querydsl APIを使用してソート式を定義する
QSort sort = QSort.by(QPerson.firstname.asc())
  .and(QSort.by(QPerson.lastname.desc()));

3.4.5. クエリ結果の制限

first または top キーワードを使用することで、クエリメソッドの結果を制限できます。これらのキーワードは互換的に使用できます。オプションの数値を top または first に追加して、返される結果の最大サイズを指定できます。数を省略すると、結果サイズ1が想定されます。次の例は、クエリサイズを制限する方法を示しています。

例 18: Top および Firstを使用したクエリの結果サイズの制限
User findFirstByOrderByLastnameAsc();

User findTopByOrderByAgeDesc();

Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);

Slice<User> findTop3ByLastname(String lastname, Pageable pageable);

List<User> findFirst10ByLastname(String lastname, Sort sort);

List<User> findTop10ByLastname(String lastname, Pageable pageable);

制限式は、Distinct キーワードもサポートしています。また、結果セットを1つのインスタンスに制限するクエリでは、Optional キーワードで結果をラップすることがサポートされています。

ページネーションまたはスライシングがクエリのページネーションの制限(および使用可能なページ数の計算)に適用される場合、制限された結果内で適用されます。

Sort パラメーターを使用して動的ソートと組み合わせて結果を制限すると、「K」最小エレメントと「K」最大エレメントの照会メソッドを表現できます。

3.4.6. コレクションまたはイテラブルを返すリポジトリメソッド

複数の結果を返すクエリメソッドは、標準のJava Iterable, List, Setを使用できます。さらに、Spring Dataの StreamableIterableのカスタム拡張、およびVavr(英語) が提供するコレクションタイプの返送をサポートしています。

Streamableをクエリメソッドの戻り値の型として使用する

Streamable は、Iterable または任意のコレクションタイプの代替として使用できます。非並列 StreamIterableにない)にアクセスするための便利なメソッド、直接 ….filter(…) および ….map(…) を要素に渡して Streamable を他の連結する機能を提供します。

例 19: Streamableを使用してクエリメソッドの結果を結合する
interface PersonRepository extends Repository<Person, Long> {
  Streamable<Person> findByFirstnameContaining(String firstname);
  Streamable<Person> findByLastnameContaining(String lastname);
}

Streamable<Person> result = repository.findByFirstnameContaining("av")
  .and(repository.findByLastnameContaining("ea"));
カスタムのストリーミング可能なラッパータイプを返す

コレクションに専用のラッパータイプを提供することは、複数の要素を返すクエリ実行結果にAPIを提供するために一般的に使用されるパターンです。通常、これらの型は、コレクションのような型を返すリポジトリメソッドを呼び出し、ラッパー型のインスタンスを手動で作成することで使用されます。Spring Dataでは、これらのラッパータイプが以下の条件を満たす場合、クエリメソッドの戻り値のタイプとしてこれらのラッパータイプを使用できるため、この追加ステップを回避できます。

  1. タイプは Streamableを実装します。

  2. この型は、Streamable を引数として取得する、of(…) または valueOf(…) という名前のコンストラクターまたは静的ファクトリーメソッドを公開します。

サンプルの使用例は次のようになります。

class Product { (1)
  MonetaryAmount getPrice() { … }
}

@RequiredArgConstructor(staticName = "of")
class Products implements Streamable<Product> { (2)

  private Streamable<Product> streamable;

  public MonetaryAmount getTotal() { (3)
    return streamable.stream() //
      .map(Priced::getPrice)
      .reduce(Money.of(0), MonetaryAmount::add);
  }
}

interface ProductRepository implements Repository<Product, Long> {
  Products findAllByDescriptionContaining(String text); (4)
}
1製品の価格にアクセスするためのAPIを公開する Product エンティティ。
2 Products.of(…) (Lombokアノテーションを介して作成されたファクトリメソッド)を介して構築できる Streamable<Product> のラッパータイプ。
3ラッパータイプは、Streamable<Product>の新しい値を計算する追加のAPIを公開します。
4そのラッパー型は、クエリメソッドの戻り型として直接使用できます。 Stremable<Product> を返し、リポジトリクライアントで手動でラップする必要はありません。
Vavrコレクションのサポート

Vavr(英語) は、Javaで関数型プログラミングの概念を取り入れるライブラリです。クエリメソッドの戻り値の型として使用できるコレクション型のカスタムセットが付属しています。

Vavrコレクションタイプ使用されるVavr実装タイプ有効なJavaソースタイプ

io.vavr.collection.Seq

io.vavr.collection.List

java.util.Iterable

io.vavr.collection.Set

io.vavr.collection.LinkedHashSet

java.util.Iterable

io.vavr.collection.Map

io.vavr.collection.LinkedHashMap

java.util.Map

最初の列の型(またはそのサブタイプ)はquerメソッドの戻り型として使用でき、実際のクエリ結果(3番目の列)のJava型に応じて実装型として使用される2番目の列の型を取得します。または、Traversable (Vavr Iterable 相当)を宣言して、実際の戻り値から実装クラスを導出できます。つまり、java.util.List はVavr List / Seqに、java.util.Set はVavr LinkedHashSet / Set などになります。

3.4.7. リポジトリメソッドのnull処理

Spring Data 2.0の時点で、個々の集約インスタンスを返すリポジトリCRUDメソッドは、Java 8の Optional を使用して、値が存在しない可能性を示します。それに加えて、Spring Dataはクエリメソッドで次のラッパータイプを返すことをサポートしています。

  • com.google.common.base.Optional

  • scala.Option

  • io.vavr.control.Option

または、クエリメソッドは、ラッパータイプをまったく使用しないことを選択できます。 nullを返すことにより、クエリ結果がないことが示されます。コレクション、コレクションの代替、ラッパー、およびストリームを返すリポジトリメソッドは、null ではなく、対応する空の表現を返すことが保証されています。詳細については、「リポジトリクエリの戻り値のタイプ」を参照してください。

Nullabilityアノテーション

Spring Frameworkのnullabilityアノテーションを使用して、リポジトリメソッドのnullability制約を表現できます。これらは、次のように、実行時にツールに優しいアプローチとオプトイン null チェックを提供します。

  • @NonNullApi (Javadoc) : パッケージレベルで使用され、パラメータと戻り値のデフォルトの動作が null 値を受け入れない、または生成しないことを宣言します。

  • @NonNull (Javadoc) : null であってはならないパラメーターまたは戻り値で使用されます( @NonNullApi が適用されるパラメーターおよび戻り値では不要です)。

  • @Nullable (Javadoc) : nullの可能性があるパラメーターまたは戻り値で使用されます。

Springアノテーションには、JSR 305(英語) アノテーション(休止状態ですが、広く普及しているJSR)でメタアノテーションが付けられています。JSR 305メタアノテーションにより、IDEAEclipse(英語) 、およびKotlin(英語) などのツールベンダーは、Springアノテーションのサポートをハードコードすることなく、一般的な方法でnull安全サポートを提供できます。クエリメソッドのnullability制約の実行時チェックを有効にするには、次の例に示すように、package-info.javaでSpringの @NonNullApi を使用して、パッケージレベルでnull禁止を有効にする必要があります。

例 20: package-info.javaで非null可能性を宣言する
@org.springframework.lang.NonNullApi
package com.acme;

null以外のデフォルトが設定されると、リポジトリクエリメソッドの呼び出しは、null許容性の制約について実行時に検証されます。クエリの実行結果が定義された制約に違反すると、例外がスローされます。これは、メソッドが null を返すが、null不可として宣言されている場合に発生します(リポジトリが存在するパッケージで定義されたアノテーションのデフォルト)。null可能結果に再度オプトインする場合は、個々のメソッドで @Nullable を選択的に使用します。このセクションの冒頭で説明した結果ラッパータイプの使用は、引き続き期待どおりに機能します。空の結果は、不在を表す値に変換されます。

次の例は、今説明したいくつかの手法を示しています。

例 21: さまざまなnull値制約を使用する
package com.acme;                                                       (1)

import org.springframework.lang.Nullable;

interface UserRepository extends Repository<User, Long> {

  User getByEmailAddress(EmailAddress emailAddress);                    (2)

  @Nullable
  User findByEmailAddress(@Nullable EmailAddress emailAdress);          (3)

  Optional<User> findOptionalByEmailAddress(EmailAddress emailAddress); (4)
}
1リポジトリは、null以外の動作を定義したパッケージ(またはサブパッケージ)にあります。
2実行されたクエリが結果を生成しない場合、EmptyResultDataAccessException をスローします。メソッドに渡された emailAddressnullの場合、IllegalArgumentException をスローします。
3実行されたクエリが結果を生成しない場合、null を返します。 emailAddressの値として null も受け入れます。
4実行されたクエリが結果を生成しない場合、Optional.empty() を返します。メソッドに渡された emailAddressnullの場合、IllegalArgumentException をスローします。
KotlinベースのリポジトリのNullability

Kotlinには、言語に組み込まれたnull可能性制約(英語) の定義があります。Kotlinコードはバイトコードにコンパイルされます。これは、メソッドシグネチャーではなく、コンパイルされたメタデータを通じてnullability制約を表現しません。 kotlin-reflect JARをプロジェクトに含めて、Kotlinのnullability制約のイントロスペクションを有効にしてください。Spring Dataリポジトリは、言語メカニズムを使用してこれらの制約を定義し、次のように同じランタイムチェックを適用します。

例 22: Kotlinリポジトリでのnull可能性制約の使用
interface UserRepository : Repository<User, String> {

  fun findByUsername(username: String): User     (1)

  fun findByFirstname(firstname: String?): User? (2)
}
1このメソッドは、パラメーターと結果の両方をnull不可(Kotlinのデフォルト)として定義します。Kotlinコンパイラーは、null をメソッドに渡すメソッド呼び出しを拒否します。クエリの実行結果が空の場合、EmptyResultDataAccessException がスローされます。
2このメソッドは、firstname パラメーターの null を受け入れ、クエリの実行で結果が生成されない場合は null を返します。

3.4.8. クエリ結果のストリーミング

Java 8 Stream<T> を戻り型として使用することにより、クエリメソッドの結果をインクリメンタルに処理できます。次の例に示すように、クエリ結果を Stream データストアにラップする代わりに、ストリーミングを実行するために特定のメソッドが使用されます。

例 23: Java 8 Stream<T>を使用したクエリの結果のストリーミング
@Query("select u from User u")
Stream<User> findAllByCustomQueryAndStream();

Stream<User> readAllByFirstnameNotNull();

@Query("select u from User u")
Stream<User> streamAllPaged(Pageable pageable);
Stream は潜在的に基礎となるデータストア固有のリソースをラップするため、使用後に閉じる必要があります。次の例に示すように、close() メソッドを使用するか、Java 7 try-with-resources ブロックを使用して、Stream を手動で閉じることができます。
例 24: Stream<T> を使用すると、try-with-resourcesブロックが生成されます
try (Stream<User> stream = repository.findAllByCustomQueryAndStream()) {
  stream.forEach(…);
}
現在、すべてのSpring Dataモジュールが戻り型として Stream<T> をサポートしているわけではありません。

3.4.9. 非同期クエリの結果

Springの非同期メソッド実行機能を使用して、リポジトリクエリを非同期に実行できます。これは、Spring TaskExecutorに送信されたタスクで実際のクエリ実行が行われている間に、メソッドが呼び出し時にすぐに戻ることを意味します。非同期クエリの実行は、リアクティブクエリの実行とは異なるため、混在させないでください。リアクティブサポートの詳細については、ストア固有のドキュメントを参照してください。次の例は、いくつかの非同期クエリを示しています。

@Async
Future<User> findByFirstname(String firstname);               (1)

@Async
CompletableFuture<User> findOneByFirstname(String firstname); (2)

@Async
ListenableFuture<User> findOneByLastname(String lastname);    (3)
1戻り型として java.util.concurrent.Future を使用します。
2戻り型としてJava 8 java.util.concurrent.CompletableFuture を使用します。
3戻り型として org.springframework.util.concurrent.ListenableFuture を使用します。

3.5. リポジトリインスタンスの作成

このセクションでは、定義されたリポジトリインターフェースのインスタンスとBean定義を作成します。そのための1つの方法は、リポジトリメカニズムをサポートする各Spring Dataモジュールに同梱されているSpring名前空間を使用することです。ただし、通常はJava構成を使用することをお勧めします。

3.5.1. XML 構成

次の例に示すように、各Spring Dataモジュールには、Springがスキャンする基本パッケージを定義できる repositories 要素が含まれています。

例 25: XMLを介したSpring Dataリポジトリの有効化
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://www.springframework.org/schema/data/jpa"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/jpa
    https://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

  <repositories base-package="com.acme.repositories" />

</beans:beans>

上記の例では、Springは、com.acme.repositories とそのすべてのサブパッケージをスキャンして、Repository またはそのサブインターフェースの1つを継承するインターフェースをスキャンするように指示されています。見つかったインターフェースごとに、インフラストラクチャは永続化テクノロジ固有の FactoryBean を登録して、クエリメソッドの呼び出しを処理する適切なプロキシを作成します。各Beanは、インターフェース名から派生したBean名で登録されているため、UserRepository のインターフェースは userRepositoryで登録されます。 base-package 属性ではワイルドカードを使用できるため、スキャンされたパッケージのパターンを定義できます。

フィルタを使う

デフォルトでは、インフラストラクチャは、設定されたベースパッケージにある永続化技術固有の Repository サブインターフェースを継承するすべてのインターフェースをピックアップし、Beanインスタンスを作成します。ただし、Beanインスタンスを作成するインターフェースをより詳細に制御したい場合があります。そのためには、<repositories /> 要素内で <include-filter /> および <exclude-filter /> 要素を使用します。セマンティクスは、Springのコンテキスト名前空間の要素とまったく同じです。詳細については、これらの要素のSpringリファレンスドキュメントを参照してください。

例:特定のインターフェースをリポジトリBeanとしてインスタンス化から除外するには、次の構成を使用できます。

例 26: exclude-filter要素を使用する
<repositories base-package="com.acme.repositories">
  <context:exclude-filter type="regex" expression=".*SomeRepository" />
</repositories>

上記の例は、SomeRepository で終わるすべてのインターフェースをインスタンス化から除外します。

3.5.2. JavaConfig

リポジトリインフラストラクチャは、JavaConfigクラスでストア固有の @Enable${store}Repositories アノテーションを使用してトリガーすることもできます。SpringコンテナーのJavaベースの構成の概要については、SpringリファレンスドキュメントのJavaConfigを参照してください。

Spring Dataリポジトリを有効にするサンプル構成は次のようになります。

例 27: アノテーションベースのリポジトリ構成のサンプル
@Configuration
@EnableJpaRepositories("com.acme.repositories")
class ApplicationConfiguration {

  @Bean
  EntityManagerFactory entityManagerFactory() {
    // …
  }
}
上記の例では、JPA固有のアノテーションを使用しています。これは、実際に使用するストアモジュールに応じて変更します。同じことが EntityManagerFactory Beanの定義にも当てはまります。ストア固有の構成について説明しているセクションを参照してください。

3.5.3. スタンドアロン使用

Springコンテナーの外部(たとえば、CDI環境)でリポジトリインフラストラクチャを使用することもできます。クラスパスにはまだSpringライブラリがいくつか必要ですが、通常、プログラムでリポジトリを設定することもできます。リポジトリサポートを提供するSpring Dataモジュールには、次のように使用できる永続化技術固有の RepositoryFactory が付属しています。

例 28: リポジトリファクトリのスタンドアロン使用
RepositoryFactorySupport factory = … // Instantiate factory here
UserRepository repository = factory.getRepository(UserRepository.class);

3.6. Spring Dataリポジトリのカスタム実装

このセクションでは、リポジトリのカスタマイズと、フラグメントが複合リポジトリを形成する方法について説明します。

クエリメソッドが別の動作を必要とする場合、またはクエリ派生によって実装できない場合は、カスタム実装を提供する必要があります。Spring Dataリポジトリを使用すると、カスタムリポジトリコードを提供し、一般的なCRUD抽象化およびクエリメソッド機能と統合できます。

3.6.1. 個々のリポジトリのカスタマイズ

リポジトリにカスタム機能を追加するには、次の例に示すように、最初にフラグメントインターフェースとカスタム機能の実装を定義する必要があります。

例 29: カスタムリポジトリ機能のインターフェース
interface CustomizedUserRepository {
  void someCustomMethod(User user);
}
例 30: カスタムリポジトリ機能の実装
class CustomizedUserRepositoryImpl implements CustomizedUserRepository {

  public void someCustomMethod(User user) {
    // Your custom implementation
  }
}
フラグメントインターフェースに対応するクラス名の最も重要な部分は、Impl 後置です。

実装自体はSpring Dataに依存せず、通常のSpring Beanにすることができます。そのため、標準の依存性注入動作を使用して、他のBean( JdbcTemplateなど)への参照を注入したり、アスペクトに参加したりすることができます。

次に、次の例に示すように、リポジトリインターフェースでフラグメントインターフェースを継承できます。

例 31: リポジトリインターフェースの変更
interface UserRepository extends CrudRepository<User, Long>, CustomizedUserRepository {

  // Declare query methods here
}

リポジトリインターフェースでフラグメントインターフェースを拡張すると、CRUDとカスタム機能が組み合わされ、クライアントで使用できるようになります。

Spring Dataリポジトリは、リポジトリ構成を形成するフラグメントを使用して実装されます。フラグメントは、ベースリポジトリ、機能的側面(QueryDslなど)、およびカスタムインターフェースとその実装です。リポジトリインターフェースにインターフェースを追加するたびに、フラグメントを追加して構成を強化します。ベースリポジトリおよびリポジトリアスペクトの実装は、各Spring Dataモジュールによって提供されます。

次の例は、カスタムインターフェースとその実装を示しています。

例 32: 実装のフラグメント
interface HumanRepository {
  void someHumanMethod(User user);
}

class HumanRepositoryImpl implements HumanRepository {

  public void someHumanMethod(User user) {
    // Your custom implementation
  }
}

interface ContactRepository {

  void someContactMethod(User user);

  User anotherContactMethod(User user);
}

class ContactRepositoryImpl implements ContactRepository {

  public void someContactMethod(User user) {
    // Your custom implementation
  }

  public User anotherContactMethod(User user) {
    // Your custom implementation
  }
}

次の例は、CrudRepositoryを継承するカスタムリポジトリのインターフェースを示しています。

例 33: リポジトリインターフェースの変更
interface UserRepository extends CrudRepository<User, Long>, HumanRepository, ContactRepository {

  // Declare query methods here
}

リポジトリは、宣言の順序でインポートされる複数のカスタム実装で構成されます。カスタム実装は、基本実装およびリポジトリの側面よりも優先度が高くなります。この順序付けにより、ベースリポジトリおよびアスペクトメソッドをオーバーライドし、2つのフラグメントが同じメソッドシグネチャーを提供する場合のあいまいさを解決できます。リポジトリフラグメントは、単一のリポジトリインターフェースでの使用に限定されません。複数のリポジトリがフラグメントインターフェースを使用し、異なるリポジトリでカスタマイズを再利用できる場合があります。

次の例は、リポジトリフラグメントとその実装を示しています。

例 34: save(…)をオーバーライドするフラグメント
interface CustomizedSave<T> {
  <S extends T> S save(S entity);
}

class CustomizedSaveImpl<T> implements CustomizedSave<T> {

  public <S extends T> S save(S entity) {
    // Your custom implementation
  }
}

次の例は、前述のリポジトリフラグメントを使用するリポジトリを示しています。

例 35: カスタマイズされたリポジトリインターフェース
interface UserRepository extends CrudRepository<User, Long>, CustomizedSave<User> {
}

interface PersonRepository extends CrudRepository<Person, Long>, CustomizedSave<Person> {
}
構成

名前空間構成を使用する場合、リポジトリインフラストラクチャは、リポジトリが見つかったパッケージのクラスをスキャンすることにより、カスタム実装フラグメントを自動検出しようとします。これらのクラスは、名前空間要素の repository-impl-postfix 属性をフラグメントインターフェース名に追加する命名規則に従う必要があります。この接尾辞のデフォルトは Implです。次の例は、デフォルトの接尾辞を使用するリポジトリと、接尾辞のカスタム値を設定するリポジトリを示しています。

例 36: 構成例
<repositories base-package="com.acme.repository" />

<repositories base-package="com.acme.repository" repository-impl-postfix="MyPostfix" />

前の例の最初の構成は、com.acme.repository.CustomizedUserRepositoryImpl と呼ばれるクラスを検索して、カスタムリポジトリ実装として機能しようとします。2番目の例では、com.acme.repository.CustomizedUserRepositoryMyPostfixを検索しようとします。

あいまいさの解決

一致するクラス名を持つ複数の実装が異なるパッケージで見つかった場合、Spring DataはBean名を使用して、使用する実装を識別します。

前に示した CustomizedUserRepository の次の2つのカスタム実装を考えると、最初の実装が使用されます。そのBean名は customizedUserRepositoryImplであり、これはフラグメントインターフェース(CustomizedUserRepository)の名前と接尾辞 Implに一致します。

例 37: あいまいな実装の解決
package com.acme.impl.one;

class CustomizedUserRepositoryImpl implements CustomizedUserRepository {

  // Your custom implementation
}
package com.acme.impl.two;

@Component("specialCustomImpl")
class CustomizedUserRepositoryImpl implements CustomizedUserRepository {

  // Your custom implementation
}

UserRepository インターフェースに @Component("specialCustom")でアノテーションを付けると、Bean名に Impl を加えたものが、com.acme.impl.twoのリポジトリ実装用に定義されたものと一致し、最初のものの代わりに使用されます。

手動接続

カスタム実装でアノテーションベースの構成とオートワイヤーのみを使用する場合、上記のアプローチは他のSpring Beanと同様に処理されるため、上手く機能します。実装フラグメントBeanに特別な接続が必要な場合、Beanを宣言し、前のセクションで説明した規則に従って名前を付けることができます。インフラストラクチャは、Bean定義を手動で作成する代わりに、名前で手動で定義したものを参照します。次の例は、カスタム実装を手動で接続する方法を示しています。

例 38: カスタム実装の手動接続
<repositories base-package="com.acme.repository" />

<beans:bean id="userRepositoryImpl" class="…">
  <!-- further configuration -->
</beans:bean>

3.6.2. ベースリポジトリをカスタマイズする

前のセクションで説明したアプローチでは、ベースリポジトリの動作をカスタマイズしてすべてのリポジトリが影響を受けるようにする場合、各リポジトリインターフェースをカスタマイズする必要があります。代わりに、すべてのリポジトリの動作を変更するために、永続化テクノロジ固有のリポジトリベースクラスを継承する実装を作成できます。このクラスは、次の例に示すように、リポジトリプロキシのカスタムベースクラスとして機能します。

例 39: カスタムリポジトリベースクラス
class MyRepositoryImpl<T, ID>
  extends SimpleJpaRepository<T, ID> {

  private final EntityManager entityManager;

  MyRepositoryImpl(JpaEntityInformation entityInformation,
                          EntityManager entityManager) {
    super(entityInformation, entityManager);

    // Keep the EntityManager around to used from the newly introduced methods.
    this.entityManager = entityManager;
  }

  @Transactional
  public <S extends T> S save(S entity) {
    // implementation goes here
  }
}
このクラスには、ストア固有のリポジトリファクトリ実装が使用するスーパークラスのコンストラクターが必要です。リポジトリの基本クラスに複数のコンストラクターがある場合は、EntityInformation とストア固有のインフラストラクチャオブジェクト( EntityManager またはテンプレートクラスなど)を取得するコンストラクターをオーバーライドします。

最後のステップは、Spring Dataインフラストラクチャーにカスタマイズされたリポジトリ基本クラスを認識させることです。Java構成では、次の例に示すように、@Enable${store}Repositories アノテーションの repositoryBaseClass 属性を使用してこれを行うことができます。

例 40: JavaConfigを使用したカスタムリポジトリベースクラスの構成
@Configuration
@EnableJpaRepositories(repositoryBaseClass = MyRepositoryImpl.class)
class ApplicationConfiguration { … }

次の例に示すように、XML名前空間で対応する属性を使用できます。

例 41: XMLを使用したカスタムリポジトリベースクラスの構成
<repositories base-package="com.acme.repository"
     base-class="….MyRepositoryImpl" />

3.7. 集約ルートからのイベントの公開

リポジトリによって管理されるエンティティは、集約ルートです。ドメイン駆動設計アプリケーションでは、これらの集約ルートは通常、ドメインイベントを発行します。Spring Dataは、@DomainEvents と呼ばれるアノテーションを提供します。これは、次の例に示すように、集約パブリケーションのメソッドで使用して、その公開をできるだけ簡単にすることができます。

例 42: 集約ルートからのドメインイベントの公開
class AnAggregateRoot {

    @DomainEvents (1)
    Collection<Object> domainEvents() {
        // … return events you want to get published here
    }

    @AfterDomainEventPublication (2)
    void callbackMethod() {
       // … potentially clean up domain events list
    }
}
1 @DomainEvents を使用するメソッドは、単一のイベントインスタンスまたはイベントのコレクションを返すことができます。引数を取ることはできません。
2すべてのイベントが公開された後、@AfterDomainEventPublicationアノテーションが付けられたメソッドがあります。(他の用途の中でも)公開されるイベントのリストを潜在的に消去するために使用できます。

これらのメソッドは、Spring Dataリポジトリの save(…) メソッドの1つが呼び出されるたびに呼び出されます。

3.8. Spring Data拡張

このセクションでは、さまざまなコンテキストでSpring Dataを使用できるようにする一連のSpring Data拡張について説明します。現在、統合のほとんどはSpring MVCを対象としています。

3.8.1. Querydsl拡張

Querydsl(英語) は、流れるようなAPIを使用して、静的に型指定されたSQLのようなクエリの構築を可能にするフレームワークです。

次の例に示すように、いくつかのSpring Dataモジュールは QuerydslPredicateExecutorを介してQuerydslとの統合を提供します。

例 43: QuerydslPredicateExecutorインターフェース
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 を継承します。

例 44: リポジトリでのQuerydsl統合
interface UserRepository extends CrudRepository<User, Long>, QuerydslPredicateExecutor<User> {
}

上記の例では、次の例に示すように、Querydsl Predicate インスタンスを使用してタイプセーフクエリを記述できます。

Predicate predicate = user.firstname.equalsIgnoreCase("dave")
	.and(user.lastname.startsWithIgnoreCase("mathews"));

userRepository.findAll(predicate);

3.8.2. Web サポート

このセクションには、Spring Data Commonsの現在(およびそれ以降)のバージョンに実装されているSpring Data Webサポートのドキュメントが含まれています。新しく導入されたサポートは多くの点を変更するため、[web.legacy]で以前の動作のドキュメントを保持しました。

リポジトリプログラミングモデルをサポートするSpring Dataモジュールには、さまざまなWebサポートが付属しています。Web関連のコンポーネントでは、Spring MVC JARがクラスパス上にある必要があります。それらのいくつかはSpring HATEOAS(GitHub) との統合さえ提供します。一般に、次の例に示すように、JavaConfig構成クラスで @EnableSpringDataWebSupport アノテーションを使用することにより、統合サポートが有効になります。

例 45: Spring Data Webサポートの有効化
@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
class WebConfiguration {}

@EnableSpringDataWebSupport アノテーションは、少し説明するいくつかのコンポーネントを登録します。また、クラスパスでSpring HATEOASを検出し、存在する場合は同様に統合コンポーネントを登録します。

あるいは、XML構成を使用する場合、次の例( SpringDataWebConfigurationの場合)に示すように、SpringDataWebConfiguration または HateoasAwareSpringDataWebConfiguration をSpring Beanとして登録します。

例 46: XMLでSpring Data Webサポートを有効にする
<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" />
基本的なWebサポート

前のセクションで示した構成は、いくつかの基本的なコンポーネントを登録します。

  • Spring MVCが要求パラメーターまたはパス変数からリポジトリ管理ドメインクラスのインスタンスを解決できるようにする DomainClassConverter

  • Spring MVCが要求パラメーターから Pageable および Sort インスタンスを解決できるようにする HandlerMethodArgumentResolver 実装。

DomainClassConverter

DomainClassConverter では、Spring MVCコントローラーメソッドシグネチャーでドメインタイプを直接使用できるため、次の例に示すように、リポジトリを介してインスタンスを手動で検索する必要はありません。

例 47: メソッドシグネチャーでドメインタイプを使用する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

前のセクションで示した構成スニペットは、PageableHandlerMethodArgumentResolverSortHandlerMethodArgumentResolverのインスタンスも登録します。次の例に示すように、登録により、Pageable および Sort が有効なコントローラーメソッド引数として有効になります。

例 48: コントローラーメソッドの引数としてPageableを使用する
@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 インスタンスを取得しようとします。

表 1: Pageable インスタンスについて評価されたリクエストパラメータ

page

取得するページ。0からインデックス付けされ、デフォルトは0です。

size

取得するページのサイズ。デフォルトは20です。

sort

フォーマット property,property(,ASC|DESC)でソートする必要があるプロパティ。デフォルトのソート方向は昇順です。方向を切り替える場合は、複数の sort パラメーターを使用します(例: ?sort=firstname&sort=lastname,asc)。

この動作をカスタマイズするには、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_pagething2_page などを入力する必要があります。

メソッドに渡されるデフォルト PageablePageRequest.of(0, 20) と同等ですが、Pageable パラメーターで @PageableDefault アノテーションを使用してカスタマイズできます。

Pageableのハイパーメディアサポート

Spring HATEOASには、クライアントがページを簡単にナビゲートできるようにするためのリンクだけでなく、必要な Page メタデータと Page インスタンスのコンテンツを充実させる表現モデルクラス(PagedResources)が付属しています。Pageから PagedResources への変換は、PagedResourcesAssemblerと呼ばれるSpring HATEOAS ResourceAssembler インターフェースの実装によって行われます。次の例は、PagedResourcesAssembler をコントローラーメソッドの引数として使用する方法を示しています。

例 49: コントローラーメソッドの引数としてPagedResourcesAssemblerを使用する
@Controller
class PersonController {

  @Autowired PersonRepository repository;

  @RequestMapping(value = "/persons", method = RequestMethod.GET)
  HttpEntity<PagedResources<Person>> persons(Pageable pageable,
    PagedResourcesAssembler assembler) {

    Page<Person> persons = repository.findAll(pageable);
    return new ResponseEntity<>(assembler.toResources(persons), HttpStatus.OK);
  }
}

前の例に示すように構成を有効にすると、PagedResourcesAssembler をコントローラーメソッドの引数として使用できます。 toResources(…) を呼び出すと、次の効果があります。

  • Page のコンテンツは、PagedResources インスタンスのコンテンツになります。

  • PagedResources オブジェクトは PageMetadata インスタンスをアタッチし、Page および基礎となる PageRequestからの情報が取り込まれます。

  • PagedResources には、ページの状態に応じて、prev および next リンクが添付される場合があります。リンクは、メソッドがマップするURIを指します。メソッドに追加されたページネーションパラメータは、PageableHandlerMethodArgumentResolver の設定と一致して、リンクを後で解決できるようにします。

データベースに30個のPersonインスタンスがあるとします。これで、リクエスト(GET http://localhost:8080/persons)をトリガーして、次のような出力を表示できます。

{ "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
  }
}

アセンブラーが正しいURIを生成し、デフォルトの構成を選択して、今後の要求のためにパラメーターを Pageable に解決することがわかります。つまり、その構成を変更すると、リンクは自動的にその変更を順守します。デフォルトでは、アセンブラはそれが呼び出されたコントローラーメソッドを指しますが、PagedResourcesAssembler.toResource(…) メソッドをオーバーロードするページネーションリンクを構築するためのベースとして使用されるカスタム Link を渡すことでカスタマイズできます。

Webデータバインディングのサポート

Spring Dataプロジェクション([投影]で説明)は、次の例に示すように、JSONPath(英語) 式(ジェイウェイJsonPath(GitHub) またはXPath(英語) 式が必要(XmlBeam(英語) が必要))を使用して、受信要求ペイロードをバインドするために使用できます。

例 50: JSONPathまたはXPath式を使用したHTTPペイロードバインディング
@ProjectedPayload
public interface UserPayload {

  @XBRead("//firstname")
  @JsonPath("$..firstname")
  String getFirstname();

  @XBRead("/lastname")
  @JsonPath({ "$.lastname", "$.user.lastname" })
  String getLastname();
}

前の例に示した型は、Spring MVCハンドラーメソッドの引数として、または RestTemplateのメソッドの1つで 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を使用して実行できます。

型情報は通常、メソッドの戻り値型から解決されます。その情報は必ずしもドメインタイプと一致しないため、QuerydslPredicateroot 属性を使用することをお勧めします。

次の例は、メソッドシグネチャーで @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

これらのバインディングは、@QuerydslPredicatebindings 属性を介して、または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 解決から除外します。

3.8.3. リポジトリポピュレーター

Spring JDBCモジュールを使用している場合は、DataSource にSQLスクリプトを取り込むサポートに精通していると思われます。リポジトリレベルでも同様の抽象化を使用できますが、ストアに依存しないため、SQLをデータ定義言語として使用しません。ポピュレーターは、XML(SpringのOXM抽象化による)およびJSON(Jacksonによる)をサポートして、リポジトリに取り込むデータを定義します。

次の内容のファイル data.json があると仮定します。

例 51: JSONで定義されたデータ
[ { "_class" : "com.acme.Person",
 "firstname" : "Dave",
  "lastname" : "Matthews" },
  { "_class" : "com.acme.Person",
 "firstname" : "Carter",
  "lastname" : "Beauford" } ]

Spring Data Commonsで提供されるリポジトリ名前空間のpopulator要素を使用して、リポジトリにデータを追加できます。上記のデータをPersonRepositoryに取り込むには、次のようなポピュレーターを宣言します。

例 52: Jacksonリポジトリポピュレーターの宣言
<?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でリポジトリポピュレーターを非整列化する方法を示しています。

例 53: 非整列化リポジトリポピュレーターの宣言 (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>

リファレンスドキュメント

4. JDBCリポジトリ

この章では、JDBCのリポジトリサポートの特殊性について説明します。これは、Spring Dataリポジトリの操作で説明されているコアリポジトリサポートに基づいています。そこで説明されている基本的な概念をしっかりと理解している必要があります。

4.1. なぜSpring Data JDBCなのか?

Javaの世界におけるリレーショナルデータベースの主な永続化APIは、確かにJPAであり、独自のSpring Dataモジュールを持っています。なぜ別のものがあるのでしょうか?

JPAは、開発者を支援するために多くのことを行います。とりわけ、エンティティへの変更を追跡します。遅延読み込みを行います。さまざまなオブジェクト構造を、同等の幅広いデータベース設計にマッピングできます。

これはすばらしいことで、多くのことが非常に簡単になります。基本的なJPAチュートリアルを参照してください。しかし、JPAが特定のことを行う理由については、しばしば混乱を招きます。また、JPAでは概念的に非常に単純なことがかなり難しくなります。

Spring Data JDBCは、以下の設計上の決定を受け入れることにより、概念的にはるかにシンプルになることを目指しています。

  • エンティティをロードすると、SQLステートメントが実行されます。これが完了すると、エンティティが完全にロードされます。遅延読み込みやキャッシュは行われません。

  • エンティティを保存すると、保存されます。そうしない場合、そうではありません。ダーティトラッキングやセッションはありません。

  • エンティティをテーブルにマッピングする方法の簡単なモデルがあります。おそらく、かなり単純な場合にのみ機能します。気に入らない場合は、独自の戦略をコーディングする必要があります。Spring Data JDBCは、アノテーションを使用して戦略をカスタマイズするための非常に限られたサポートのみを提供します。

4.2. ドメイン駆動設計およびリレーショナルデータベース。

すべてのSpring Dataモジュールは、ドメインドリブンデザインの「リポジトリ」、「集約」、および「集約ルート」の概念に触発されています。これらは、Spring Data JDBCにとってさらに重要な可能性があります。これは、リレーショナルデータベースを操作する際の通常の慣行にある程度反するからです。

集約は、アトミックな変更間で一貫性が保証されるエンティティのグループです。古典的な例は、OrderOrderItemsです。 Order のプロパティ(たとえば、numberOfItemsOrderItemsの実際の数と一貫性があります)は、変更が加えられても一貫性を保ちます。

集合体全体の参照は常に一貫しているとは限りません。最終的に一貫性が保証されます。

各アグリゲートには、アグリゲートのエンティティの1つであるアグリゲートルートが1つだけあります。アグリゲートは、そのアグリゲートルートのメソッドを介してのみ操作されます。これらは、前述のアトミックな変更です。

リポジトリは、特定のタイプのすべての集約のコレクションのように見える永続ストアの抽象化です。一般的に、Spring Dataの場合、これは集約ルートごとに1つの Repository が必要であることを意味します。さらに、Spring Data JDBCの場合、これは、集約ルートから到達可能なすべてのエンティティがその集約ルートの一部と見なされることを意味します。Spring Data JDBCは、アグリゲートのみがアグリゲートの非ルートエンティティを格納するテーブルへの外部キーを持ち、他のエンティティが非ルートエンティティを指すことはないと想定しています。

現在の実装では、集約ルートから参照されるエンティティは削除され、Spring Data JDBCによって再作成されます。

データベースの作業および設計のスタイルに一致する実装で、リポジトリメソッドを上書きできます。

4.3. アノテーションベースの構成

次の例に示すように、Spring Data JDBCリポジトリのサポートは、Java構成を介したアノテーションによってアクティブにできます。

例 54: Java構成を使用したSpring Data JDBCリポジトリ
@Configuration
@EnableJdbcRepositories
class ApplicationConfig {

  @Bean
  public DataSource dataSource() {

    EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
    return builder.setType(EmbeddedDatabaseType.HSQL).build();
  }

}

前の例の構成クラスは、spring-jdbcEmbeddedDatabaseBuilder APIを使用して、組み込みHSQLデータベースをセットアップします。 @EnableJdbcRepositoriesを使用してSpring Data JDBCリポジトリをアクティブ化します。基本パッケージが構成されていない場合、構成クラスが存在するパッケージが使用されます。

4.4. 永続化エンティティ

CrudRepository.save(…) メソッドを使用して、集計を保存できます。集約が新しい場合、集約ルートの挿入が発生し、その後に直接または間接的に参照されるすべてのエンティティの挿入ステートメントが続きます。

集約ルートが新規ではない場合、すべての参照エンティティが削除され、集約ルートが更新され、すべての参照エンティティが再度挿入されます。インスタンスが新しいかどうかは、インスタンスの状態の一部であることに注意してください。

このアプローチには、明らかな欠点がいくつかあります。参照されたエンティティのうち実際に変更されたものがわずかしかない場合、削除と挿入は無駄です。このプロセスは改善される可能性があり、おそらく改善される予定ですが、Spring Data JDBCが提供できるものには特定の制限があります。集約の以前の状態はわかりません。そのため、更新プロセスは常にデータベースで見つかったものをすべて取得し、saveメソッドに渡されたエンティティの状態に変換する必要があります。

4.4.1. オブジェクトマッピングの基礎

このセクションでは、Spring Dataオブジェクトマッピング、オブジェクト作成、フィールドとプロパティへのアクセス、可変性と不変性の基礎について説明します。このセクションは、基になるデータストア(JPAなど)のオブジェクトマッピングを使用しないSpring Dataモジュールにのみ適用されることに注意してください。また、インデックス、列名やフィールド名のカスタマイズなど、ストア固有のオブジェクトマッピングについては、ストア固有のセクションを参照してください。

Spring Dataオブジェクトマッピングの中心的なロールは、ドメインオブジェクトのインスタンスを作成し、ストアネイティブデータ構造をそれらにマッピングすることです。つまり、2つの基本的な手順が必要です。

  1. 公開されたコンストラクターの1つを使用したインスタンスの作成。

  2. すべての公開されたプロパティを具体化するインスタンスの設定。

オブジェクト作成

Spring Dataは、そのタイプのオブジェクトの具体化に使用される永続エンティティのコンストラクターを自動的に検出しようとします。解決アルゴリズムは次のように機能します。

  1. 引数なしのコンストラクターがある場合は、それが使用されます。他のコンストラクターは無視されます。

  2. 引数を取る単一のコンストラクターがある場合は、それが使用されます。

  3. 引数を取る複数のコンストラクターがある場合、Spring Dataが使用するコンストラクターに @PersistenceConstructorのアノテーションを付ける必要があります。

値の解決は、コンストラクターの引数名がエンティティのプロパティ名と一致することを前提としています。つまり、マッピングのすべてのカスタマイズ(異なるデータストア列またはフィールド名など)を含む、プロパティが設定されるかのように解決が実行されます。また、これには、クラスファイルで使用可能なパラメーター名情報、またはコンストラクターに存在する @ConstructorProperties アノテーションのいずれかが必要です。

値の解像度は、ストア固有のSpEL式を使用したSpring Frameworkの @Value 値アノテーションを使用してカスタマイズできます。詳細については、ストア固有のマッピングに関するセクションを参照してください。

オブジェクト作成の詳細

リフレクションのオーバーヘッドを回避するために、Spring Dataオブジェクトの作成では、デフォルトで実行時に生成されるファクトリクラスを使用します。これにより、ドメインクラスコンストラクターが直接呼び出されます。つまりこの例のタイプ:

class Person {
  Person(String firstname, String lastname) { … }
}

実行時にこれと意味的に同等のファクトリクラスを作成します。

class PersonObjectInstantiator implements ObjectInstantiator {

  Object newInstance(Object... args) {
    return new Person((String) args[0], (String) args[1]);
  }
}

これにより、リフレクションよりも約10%パフォーマンスが向上します。ドメインクラスがこのような最適化の対象となるには、一連の制約に従う必要があります。

  • プライベートクラスであってはなりません

  • 非静的内部クラスであってはなりません

  • CGLibプロキシクラスであってはなりません

  • Spring Dataで使用されるコンストラクターはプライベートであってはなりません

これらの基準のいずれかが一致する場合、Spring Dataはリフレクションを介してエンティティのインスタンス化にフォールバックします。

プロパティ設定

エンティティのインスタンスが作成されると、Spring Dataはそのクラスの残りのすべての永続プロパティを設定します。エンティティのコンストラクターによって既に入力されていない場合(つまり、コンストラクターの引数リストを介して使用される場合)、IDプロパティが最初に入力され、循環オブジェクト参照の解決が可能になります。その後、コンストラクターによってまだ設定されていないすべての非一時的なプロパティがエンティティインスタンスに設定されます。そのために、次のアルゴリズムを使用します。

  1. プロパティが不変であるがウィザーメソッドを公開している場合(以下を参照)、ウィザーを使用して、新しいプロパティ値を持つ新しいエンティティインスタンスを作成します。

  2. プロパティアクセス(つまり、getterおよびsetterを介したアクセス)が定義されている場合、setterメソッドを呼び出しています。

  3. デフォルトでは、フィールド値を直接設定します。

プロパティ設定の詳細

オブジェクト構築の最適化と同様に、Spring Dataランタイム生成のアクセサークラスを使用して、エンティティインスタンスと対話します。

class Person {

  private final Long id;
  private String firstname;
  private @AccessType(Type.PROPERTY) String lastname;

  Person() {
    this.id = null;
  }

  Person(Long id, String firstname, String lastname) {
    // Field assignments
  }

  Person withId(Long id) {
    return new Person(id, this.firstname, this.lastame);
  }

  void setLastname(String lastname) {
    this.lastname = lastname;
  }
}
例 55: 生成されたプロパティアクセサー
class PersonPropertyAccessor implements PersistentPropertyAccessor {

  private static final MethodHandle firstname;              (2)

  private Person person;                                    (1)

  public void setProperty(PersistentProperty property, Object value) {

    String name = property.getName();

    if ("firstname".equals(name)) {
      firstname.invoke(person, (String) value);             (2)
    } else if ("id".equals(name)) {
      this.person = person.withId((Long) value);            (3)
    } else if ("lastname".equals(name)) {
      this.person.setLastname((String) value);              (4)
    }
  }
}
1PropertyAccessorは、基礎となるオブジェクトの可変インスタンスを保持します。これは、そうでなければ不変のプロパティの変更を可能にするためです。
2デフォルトでは、Spring Dataはフィールドアクセスを使用してプロパティ値を読み書きします。 private フィールドの可視性ルールに従って、MethodHandles はフィールドとの対話に使用されます。
3クラスは、識別子の設定に使用される withId(…) メソッドを公開します。インスタンスがデータストアに挿入され、識別子が生成されたとき。 withId(…) を呼び出すと、新しい Person オブジェクトが作成されます。後続のすべての変更は、新しいインスタンスで行われ、前のインスタンスは変更されません。
4property-accessを使用すると、MethodHandlesを使用せずに直接メソッドを呼び出すことができます。

これにより、リフレクションよりも約25%パフォーマンスが向上します。ドメインクラスがこのような最適化の対象となるには、一連の制約に従う必要があります。

  • タイプは、デフォルトまたは java パッケージに存在してはなりません。

  • 型とそのコンストラクターは publicでなければなりません

  • 内部クラスである型は staticでなければなりません。

  • 使用されるJavaランタイムは、元の ClassLoaderでクラスを宣言できるようにする必要があります。Java 9以降には特定の制限があります。

デフォルトでは、Spring Dataは生成されたプロパティアクセサーを使用しようとし、制限が検出された場合はリフレクションベースのものにフォールバックします。

次のエンティティを見てみましょう。

例 56: サンプルエンティティ
class Person {

  private final @Id Long id;                                                (1)
  private final String firstname, lastname;                                 (2)
  private final LocalDate birthday;
  private final int age; (3)

  private String comment;                                                   (4)
  private @AccessType(Type.PROPERTY) String remarks;                        (5)

  static Person of(String firstname, String lastname, LocalDate birthday) { (6)

    return new Person(null, firstname, lastname, birthday,
      Period.between(birthday, LocalDate.now()).getYears());
  }

  Person(Long id, String firstname, String lastname, LocalDate birthday, int age) { (6)

    this.id = id;
    this.firstname = firstname;
    this.lastname = lastname;
    this.birthday = birthday;
    this.age = age;
  }

  Person withId(Long id) {                                                  (1)
    return new Person(id, this.firstname, this.lastname, this.birthday);
  }

  void setRemarks(String remarks) {                                         (5)
    this.remarks = remarks;
  }
}
1identifierプロパティはfinalですが、コンストラクターで null に設定されます。クラスは、識別子の設定に使用される withId(…) メソッドを公開します。インスタンスがデータストアに挿入され、識別子が生成されたとき。元の Person インスタンスは、新しいインスタンスが作成されるときに変更されません。通常、ストア管理される他のプロパティにも同じパターンが適用されますが、永続化操作のために変更する必要がある場合があります。
2 firstname および lastname プロパティは、getterを介して潜在的に公開される通常の不変のプロパティです。
3 age プロパティは不変ですが、birthday プロパティから派生しています。示されている設計では、Spring Dataは宣言された唯一のコンストラクターを使用するため、データベース値はデフォルト設定よりも優先されます。計算が優先されることを意図している場合でも、このコンストラクターがパラメーターとして age をとることが重要です(それを無視する可能性がある)。そうしないと、プロパティ生成ステップは年齢フィールドを設定しようとし、不変であり、枯れています。
4 comment プロパティは可変であり、フィールドを直接設定することで入力されます。
5 remarks プロパティは可変であり、comment フィールドを直接設定するか、setterメソッドを呼び出して設定します。
6このクラスは、オブジェクト作成用のファクトリメソッドとコンストラクターを公開します。ここでの核となる考え方は、追加のコンストラクターの代わりにファクトリーメソッドを使用して、@PersistenceConstructorによるコンストラクターの明確化の必要性を回避することです。代わりに、プロパティのデフォルト設定はファクトリメソッド内で処理されます。
一般的な推奨事項
  • 不変オブジェクトに固執しようとする — 不変オブジェクトは、オブジェクトを具体化するのはコンストラクターのみを呼び出すだけなので、簡単に作成できます。また、これにより、クライアントオブジェクトがオブジェクトの状態を操作できるようにするsetterメソッドがドメインオブジェクトに散らばるのを防ぎます。それらが必要な場合は、同じ場所に配置された限られたタイプでのみ呼び出せるように、パッケージを保護することをお勧めします。コンストラクターのみの実体化は、プロパティの設定よりも最大30%高速です。

  • すべての引数のコンストラクターを提供する -エンティティを不変の値としてモデル化できない、またはしたくない場合でも、エンティティのすべてのプロパティを引数として取得するコンストラクターを提供することには価値があります。最適なパフォーマンスのためにプロパティの設定をスキップするオブジェクトマッピング。

  • @PersistenceConstructorを回避するために、オーバーロードされたコンストラクターの代わりにファクトリーメソッドを使用します — 最適なパフォーマンスに必要なすべての引数コンストラクターでは、通常、自動生成識別子などを省略したアプリケーションユースケース固有のコンストラクターを公開します。これらのall-argsコンストラクターのバリアントを公開する静的ファクトリーメソッド。

  • 生成されたインスタンス生成クラスとプロパティアクセッサクラスを使用できるようにする制約を必ず守ってください。

  • 生成される識別子については、ウィザーメソッドと組み合わせてfinalフィールドを引き続き使用します

  • Lombokを使用してボイラープレートコードを回避します — 永続化操作は通常、すべての引数を取るコンストラクターを必要とするため、その宣言はフィールド割り当てに対するボイラープレートパラメーターの退屈な繰り返しとなりますが、Lombokの @AllArgsConstructorを使用することで回避することができます。

Kotlinサポート

Spring Dataは、Kotlinの仕様を適合させて、オブジェクトの作成と変更を可能にします。

Kotlinオブジェクトの作成

Kotlinクラスはインスタンス化がサポートされており、すべてのクラスはデフォルトで不変であり、可変プロパティを定義するには明示的なプロパティ宣言が必要です。次の data クラス Personを検討してください。

data class Person(val id: String, val name: String)

上記のクラスは、明示的なコンストラクターを持つ典型的なクラスにコンパイルされます。別のコンストラクターを追加してこのクラスをカスタマイズし、@PersistenceConstructor でアノテーションを付けてコンストラクターの設定を示します。

data class Person(var id: String, val name: String) {

    @PersistenceConstructor
    constructor(id: String) : this(id, "unknown")
}

Kotlinは、パラメーターが提供されない場合にデフォルト値を使用できるようにすることで、パラメーターのオプションをサポートしています。Spring Dataがパラメーターのデフォルト設定を持つコンストラクターを検出した場合、データストアが値を提供しない(または単に nullを返す)場合、Kotlinはパラメーターのデフォルト設定を適用できるため、これらのパラメーターは存在しません。 nameのパラメーターのデフォルト設定を適用する次のクラスを検討してください。

data class Person(var id: String, val name: String = "unknown")

name パラメーターが結果の一部ではないか、その値が nullであるたびに、nameunknownにデフォルト設定されます。

Kotlinデータクラスのプロパティ設定

Kotlinでは、すべてのクラスはデフォルトで不変であり、可変プロパティを定義するには明示的なプロパティ宣言が必要です。次の data クラス Personを検討してください。

data class Person(val id: String, val name: String)

このクラスは事実上不変です。Kotlinは、既存のオブジェクトからすべてのプロパティ値をコピーし、引数として提供されたプロパティ値をメソッドに適用する新しいオブジェクトインスタンスを作成する copy(…) メソッドを生成するため、新しいインスタンスを作成できます。

4.4.2. エンティティでサポートされているタイプ

現在、次のタイプのプロパティがサポートされています。

  • すべてのプリミティブ型とそれらのボックス化された型 ( int, float, Integer, Floatなど)

  • 列挙型は名前にマップされます。

  • String

  • java.util.Date, java.time.LocalDate, java.time.LocalDateTimeおよび java.time.LocalTime

  • データベースでサポートされている場合、上記のタイプの配列とコレクションは、配列タイプの列にマップできます。

  • データベースドライバーが受け入れるもの。

  • 他のエンティティへの参照。これらは、1対1の関係、または埋め込み型と見なされます。1対1の関係エンティティが id 属性を持つことはオプションです。参照されたエンティティのテーブルには、参照しているエンティティのテーブルと同じ名前の追加の列があると予想されます。 NamingStrategy.getReverseColumnName(PersistentPropertyPathExtension path)を実装することにより、この名前を変更できます。埋め込みエンティティには idは必要ありません。存在する場合は無視されます。

  • Set<some entity> は1対多の関係と見なされます。参照されたエンティティのテーブルには、参照しているエンティティのテーブルと同じ名前の追加の列があると予想されます。 NamingStrategy.getReverseColumnName(PersistentPropertyPathExtension path)を実装することにより、この名前を変更できます。

  • Map<simple type, some entity> は、修飾された1対多の関係と見なされます。参照されるエンティティのテーブルには、2つの追加の列が必要です。1つは外部キーの参照エンティティのテーブルと同じ名前で、もう1つはマップキーの同じ名前と追加の _key サフィックスです。 NamingStrategy.getReverseColumnName(PersistentPropertyPathExtension path) および NamingStrategy.getKeyColumn(RelationalPersistentProperty property)をそれぞれ実装することにより、この動作を変更できます。あるいは、@MappedCollection(idColumn="your_column_name", keyColumn="your_key_column_name")を使用して属性にアノテーションを付けることもできます

  • List<some entity>Map<Integer, some entity>としてマップされます。

参照エンティティの処理は制限されています。これは、上記の集約ルートの考え方に基づいています。別のエンティティを参照する場合、そのエンティティは定義上、集計の一部です。そのため、参照を削除すると、以前に参照されたエンティティが削除されます。これは、参照が1-1または1-nであるが、n-1またはn-mではないことも意味します。

n-1またはn-mの参照がある場合、定義により、2つの別個の集計を処理します。それらの間の参照は単純な id 値としてエンコードする必要があり、Spring Data JDBCと適切にマッピングする必要があります。

4.4.3. カスタムコンバーター

AbstractJdbcConfiguration から構成を継承し、メソッド jdbcCustomConversions()を上書きすることにより、デフォルトでサポートされていないタイプのカスタムコンバーターを登録できます。

@Configuration
public class DataJdbcConfiguration extends AbstractJdbcConfiguration {

    @Override
    public JdbcCustomConversions jdbcCustomConversions() {

      return new JdbcCustomConversions(Collections.singletonList(TimestampTzToDateConverter.INSTANCE));

    }

    @ReadingConverter
    enum TimestampTzToDateConverter implements Converter<TIMESTAMPTZ, Date> {

        INSTANCE;

        @Override
        public Date convert(TIMESTAMPTZ source) {
            //...
        }
    }
}

JdbcCustomConversions のコンストラクターは、org.springframework.core.convert.converter.Converterのリストを受け入れます。

コンバーターには、データベースへの読み取りまたはデータベースへの書き込みのみへの適用を制御するために、@ReadingConverter または @WritingConverter のアノテーションを付ける必要があります。

例のTIMESTAMPTZ は、ドメインモデルにより適したものに変換する必要があるデータベース固有のデータ型です。

JdbcValue

値変換では、JdbcValue を使用して、java.sql.Types タイプのJDBC操作に伝搬される値を強化します。型の派生を使用する代わりにJDBC固有の型を指定する必要がある場合は、カスタム書き込みコンバーターを登録します。このコンバーターは、値を実際の JDBCTypeのフィールドを持つ JdbcValue に変換する必要があります。

4.4.4. NamingStrategy

Spring Data JDBCが提供する CrudRepository の標準実装を使用する場合、特定のテーブル構造が期待されます。アプリケーションコンテキストで NamingStrategy (Javadoc) を提供することで、これを調整できます。

4.4.5. Custom table names

NamingStrategyがデータベーステーブル名と一致しない場合、 @Table (Javadoc) アノテーションを使用して名前をカスタマイズできます。このアノテーションの要素 value は、カスタムテーブル名を提供します。次の例では、MyEntity クラスをデータベースの CUSTOM_TABLE_NAME テーブルにマップします。

@Table("CUSTOM_TABLE_NAME")
public class MyEntity {
    @Id
    Integer id;

    String name;
}

4.4.6. Custom column names

NamingStrategyがデータベースの列名と一致しない場合、 @Column (Javadoc) アノテーションを使用して名前をカスタマイズできます。このアノテーションの要素 value は、カスタム列名を提供します。次の例は、MyEntity クラスの name プロパティをデータベースの CUSTOM_COLUMN_NAME 列にマップします。

public class MyEntity {
    @Id
    Integer id;

    @Column("CUSTOM_COLUMN_NAME")
    String name;
}

@MappedCollection (Javadoc) アノテーションは、参照タイプ(1対1の関係)またはセット、リスト、およびマップ(1対多の関係)で使用できます。これらすべてのタイプでは、アノテーションの value 要素を使用してカスタムを提供します。他のテーブルのid列を参照する外部キー列の名前。次の例では、MySubEntity クラスに対応するテーブルに名前列があり、関係上の理由から MyEntity idのid列があります。この MySubEntity クラスのid列の名前は、 @MappedCollection (Javadoc) アノテーションの idColumn 要素を使用してカスタマイズすることもできます。

public class MyEntity {
    @Id
    Integer id;

    @MappedCollection(idColumn = "CUSTOM_COLUMN_NAME")
    Set<MySubEntity> name;
}

public class MySubEntity {
    String name;
}

List および Map を使用する場合、List のデータセットの位置または Mapのエンティティのキー値の追加列が必要です。この追加の列名は、 @MappedCollection (Javadoc) アノテーションの keyColumn 要素を使用してカスタマイズできます。

public class MyEntity {
    @Id
    Integer id;

    @MappedCollection(idColumn = "CUSTOM_COLUMN_NAME", keyColumn = "CUSTOM_KEY_COLUMN_NAME")
    List<MySubEntity> name;
}

public class MySubEntity {
    String name;
}

4.4.7. 埋め込みエンティティ

埋め込みエンティティは、データベースにテーブルが1つしかない場合でも、javaデータモデルに値オブジェクトを保持するために使用されます。次の例では、MyEntity@Embedded アノテーションでマップされています。この結果、データベースには、id および nameEmbeddedEntity クラスの)の2つの列を持つテーブル my_entity が期待されます。

ただし、結果セット内で name 列が実際に null である場合、@EmbeddedonEmpty に従って、プロパティ embeddedEntity 全体がnullに設定されます。ネストされたすべてのプロパティが nullである場合、nullはオブジェクトになります。
この動作とは反対に、USE_EMPTY は、デフォルトコンストラクターまたは結果セットからNULL可能パラメーター値を受け入れるコンストラクターを使用して、新しいインスタンスを作成しようとします。

例 57: 埋め込みオブジェクトのサンプルコード
public class MyEntity {

    @Id
    Integer id;

    @Embedded(onEmpty = USE_NULL) (1)
    EmbeddedEntity embeddedEntity;
}

public class EmbeddedEntity {
    String name;
}
1 nullname の場合、Null s embeddedEntity USE_EMPTY を使用して、name プロパティの潜在的な null 値で embeddedEntity をインスタンス化します。

エンティティで複数回値オブジェクトが必要な場合は、@Embedded アノテーションのオプションの prefix 要素を使用してこれを実現できます。この要素はプレフィックスを表し、埋め込みオブジェクトの各列名の先頭に追加されます。

@Embedded(onEmpty = USE_NULL) および @Embedded(onEmpty = USE_EMPTY) のショートカット @Embedded.Nullable および @Embedded.Empty を使用して、冗長性を減らし、同時にJSR-305 @javax.annotation.Nonnull を同時に設定します。

public class MyEntity {

    @Id
    Integer id;

    @Embedded.Nullable (1)
    EmbeddedEntity embeddedEntity;
}
1 @Embedded(onEmpty = USE_NULL)のショートカット。

Collection または Map を含む埋め込みエンティティは、少なくとも空のコレクションまたはマップを含むため、常に空ではないと見なされます。そのようなエンティティは、@Embedded(onEmpty = USE_NULL)を使用している場合でも null になることはありません。

4.4.8. エンティティ状態検出戦略

次の表は、エンティティが新しいかどうかを検出するためにSpring Data JDBCが提供する戦略を説明しています。

テーブル 2: Spring Data JDBCでエンティティが新しいかどうかを検出するためのオプション

IDプロパティインスペクション (デフォルト)

デフォルトでは、Spring Data JDBCは指定されたエンティティの識別子プロパティをインスペクションします。識別子プロパティが nullの場合、エンティティは新しいと見なされます。それ以外の場合は、新規ではないと見なされます。

Persistableの実装

エンティティが Persistableを実装している場合、Spring Data JDBCはエンティティの isNew(…) メソッドに新しい検出を委譲します。詳細については、Javadoc(英語) を参照してください。

EntityInformationの実装

JdbcRepositoryFactory のサブクラスを作成し、getEntityInformation(…) メソッドをオーバーライドすることにより、SimpleJdbcRepository 実装で使用される EntityInformation 抽象化をカスタマイズできます。次に、JdbcRepositoryFactory のカスタム実装をSpring Beanとして登録する必要があります。これはほとんど必要ないことに注意してください。詳細については、Javadocを参照してください。

4.4.9. ID生成

Spring Data JDBCは、IDを使用してエンティティを識別します。エンティティのIDには、Spring Dataの @Id (Javadoc) アノテーションを付ける必要があります。

データベースのID列に自動インクリメント列がある場合、生成された値はデータベースに挿入された後、エンティティに設定されます。

重要な制約の1つは、エンティティを保存した後、そのエンティティが新しいものであってはならないことです。エンティティが新しいかどうかは、エンティティの状態の一部であることに注意してください。自動インクリメント列では、ID列の値を使用してSpring DataによってIDが設定されるため、これは自動的に行われます。自動インクリメント列を使用していない場合は、BeforeSave リスナーを使用して、エンティティのIDを設定できます(このドキュメントで後述)。

4.5. クエリメソッド

このセクションでは、Spring Data JDBCの実装と使用に関する特定の情報を提供します。

4.5.1. クエリ検索戦略

JDBCモジュールは、@Query アノテーションの文字列としてのみクエリを手動で定義することをサポートします。メソッドの名前からクエリを派生させることは現在サポートされていません。

4.5.2. @Queryを使用する

次の例は、@Query を使用してクエリメソッドを宣言する方法を示しています。

例 58: @Queryを使用してクエリメソッドを宣言する
public interface UserRepository extends CrudRepository<User, Long> {

  @Query("select firstName, lastName from User u where u.emailAddress = :email")
  User findByEmailAddress(@Param("email") String email);
}
Springは、-parameters コンパイラフラグに基づくJava 8のパラメーター名の検出を完全にサポートしています。デバッグ情報の代替としてビルドでこのフラグを使用することにより、名前付きパラメーターの @Param アノテーションを省略できます。
Spring Data JDBCは、名前付きパラメーターのみをサポートします。
カスタム RowMapper

@Query(rowMapperClass = …​.) を使用するか、メソッド戻り型ごとに RowMapperMap Beanを登録して RowMapper を登録することにより、使用する RowMapper を構成できます。次の例は、RowMappersを登録する方法を示しています。

@Bean
RowMapperMap rowMappers() {
	return new ConfigurableRowMapperMap() //
		.register(Person.class, new PersonRowMapper()) //
		.register(Address.class, new AddressRowMapper());
}

メソッドに使用する RowMapper を決定するときは、メソッドの戻り値の型に基づいて、次の手順に従います。

  1. タイプが単純タイプの場合、RowMapper は使用されません。

    代わりに、クエリは単一の列を持つ単一の行を返すことが期待され、戻り値の型への変換がその値に適用されます。

  2. RowMapperMap のエンティティクラスは、問題の戻り値型のスーパークラスまたはインターフェースであるものが見つかるまで繰り返されます。そのクラスに登録された RowMapper が使用されます。

    反復は登録順に行われるため、特定のタイプの後に、より一般的なタイプを登録するようにしてください。

該当する場合、コレクションや Optional などのラッパータイプはアンラップされます。戻り型 Optional<Person> は、前のプロセスで Person タイプを使用します。

クエリの変更

次の例に示すように、@Modifying on queryメソッドを使用して、クエリを変更クエリとしてマークできます。

@Modifying
@Query("UPDATE DUMMYENTITY SET name = :name WHERE id = :id")
boolean updateName(@Param("id") Long id, @Param("name") String name);

以下の戻り型を指定できます。

  • void

  • int (更新されたレコード数)

  • boolean (レコードが更新されたかどうか)

4.6. MyBatis統合

CRUD操作とクエリメソッドの実行は、MyBatisに委譲できます。このセクションでは、Spring Data JDBCを構成してMyBatisと統合する方法、および照会の実行とライブラリへのマッピングを引き継ぐために従うべき規則を説明します。

4.6.1. 構成

MyBatisをSpring Data JDBCに適切にプラグインする最も簡単な方法は、MyBatisJdbcConfiguration をアプリケーション構成にインポートすることです。

@Configuration
@EnableJdbcRepositories
@Import(MyBatisJdbcConfiguration.class)
class Application {

  @Bean
  SqlSessionFactoryBean sqlSessionFactoryBean() {
    // Configure MyBatis here
  }
}

ご覧のとおり、MyBatisJdbcConfigurationApplicationContext で最終的に利用可能になるために SqlSession Beanに依存しているため、宣言する必要があるのは SqlSessionFactoryBean だけです。

4.6.2. 使用規則

CrudRepositoryの各操作に対して、Spring Data JDBCは複数のステートメントを実行します。アプリケーションコンテキストに SqlSessionFactory (GitHub) がある場合、Spring Dataは、各ステップで、SessionFactory がステートメントを提供するかどうかをチェックします。見つかった場合は、そのステートメント(構成されたエンティティへのマッピングを含む)が使用されます。

ステートメントの名前は、エンティティタイプの完全修飾名と Mapper. およびステートメントの種類を決定する String を連結することにより構築されます。例: org.example.User のインスタンスが挿入される場合、Spring Data JDBCは org.example.UserMapper.insertという名前のステートメントを探します。

ステートメントが実行されると、[MyBatisContext]のインスタンスが引数として渡され、さまざまな引数がステートメントで使用可能になります。

次の表は、使用可能なMyBatisステートメントについて説明しています。

名前目的このステートメントをトリガーする可能性のあるCrudRepositoryメソッド MyBatisContextで利用可能な属性

insert

単一のエンティティを挿入します。これは、集約ルートによって参照されるエンティティにも適用されます。

save, saveAll .

getInstance : 保存するインスタンス

getDomainType : 保存するエンティティのタイプ。

get(<key>) : 参照エンティティのID。<key> は、NamingStrategyによって提供される後方参照列の名前です。

update

単一のエンティティを更新します。これは、集約ルートによって参照されるエンティティにも適用されます。

save, saveAll .

getInstance : 保存するインスタンス

getDomainType : 保存するエンティティのタイプ。

delete

単一のエンティティを削除します。

delete, deleteById .

getId : 削除するインスタンスのID

getDomainType : 削除するエンティティのタイプ。

deleteAll-<propertyPath>

指定されたプロパティパスのプレフィックスとして使用されるタイプの集約ルートによって参照されるすべてのエンティティを削除します。ステートメント名のプレフィックスに使用されるタイプは、削除されるエンティティの名前ではなく、集約ルートの名前であることに注意してください。

deleteAll .

getDomainType : 削除するエンティティのタイプ。

deleteAll

プレフィックスとして使用されるタイプのすべての集約ルートを削除する

deleteAll .

getDomainType : 削除するエンティティのタイプ。

delete-<propertyPath>

指定されたpropertyPathを持つ集約ルートによって参照されるすべてのエンティティを削除する

deleteById .

getId : 参照されるエンティティが削除される集約ルートのID。

getDomainType : 削除するエンティティのタイプ。

findById

IDで集約ルートを選択する

findById .

getId : ロードするエンティティのID。

getDomainType : ロードするエンティティのタイプ。

findAll

すべての集約ルートを選択する

findAll .

getDomainType : ロードするエンティティのタイプ。

findAllById

ID値によって集約ルートのセットを選択する

findAllById .

getId : ロードするエンティティのID値のリスト。

getDomainType : ロードするエンティティのタイプ。

findAllByProperty-<propertyName>

別のエンティティによって参照されるエンティティのセットを選択します。参照エンティティのタイプがプレフィックスに使用されます。参照されるエンティティタイプは、接尾辞として使用されます。このメソッドは非推奨です。代わりに findAllByPath を使用してください

すべての find* メソッド。 findAllByPathにクエリが定義されていない場合

getId : ロードするエンティティを参照するエンティティのID。

getDomainType : ロードするエンティティのタイプ。

findAllByPath-<propertyPath>

プロパティパスを介して別のエンティティによって参照されるエンティティのセットを選択します。

すべての find* メソッド。

getIdentifier : 集約ルートのIDと、すべてのパス要素のキーおよびリストインデックスを保持する Identifier

getDomainType : ロードするエンティティのタイプ。

count

プレフィックスとして使用されるタイプの集約ルートの数を数えます

count

getDomainType : カウントする集約ルートのタイプ。

4.7. ライフサイクルイベント

Spring Data JDBCは、アプリケーションコンテキストで一致する ApplicationListener Beanに発行されるイベントをトリガーします。例:集約が保存される前に、次のリスナーが呼び出されます。

@Bean
public ApplicationListener<BeforeSave> timeStampingSaveTime() {

	return event -> {

		Object entity = event.getEntity();
		if (entity instanceof Category) {
			Category category = (Category) entity;
			category.timeStamp();
		}
	};
}

次の表に、使用可能なイベントを示します。

表3: 利用可能なイベント
イベント公開されたとき

BeforeDeleteEvent (Javadoc)

集約ルートが削除される前。

AfterDeleteEvent (Javadoc)

集約ルートが削除された後。

BeforeConvertEvent (Javadoc)

集約ルートが保存される前(つまり、挿入または更新されますが、更新または削除されるかどうかの決定が行われた後)。

BeforeSaveEvent (Javadoc)

集約ルートが保存される前(つまり、挿入または更新されますが、更新または削除されるかどうかの決定が行われた後)。

AfterSaveEvent (Javadoc)

集約ルートが保存された(つまり、挿入または更新された)後。

AfterLoadEvent (Javadoc)

データベース ResultSet から集約ルートが作成され、そのすべてのプロパティが設定された後。

ライフサイクルイベントは ApplicationEventMulticasterに依存します。SimpleApplicationEventMulticaster の場合、TaskExecutorを使用して構成できるため、イベントが処理されるときの保証はありません。

4.8. エンティティコールバック

Spring Dataインフラストラクチャは、特定のメソッドが呼び出される前後にエンティティを変更するためのフックを提供します。いわゆる EntityCallback インスタンスは、コールバック形式のエンティティをチェックし、潜在的に変更する便利な方法を提供します。
EntityCallback は、特化した ApplicationListenerに非常によく似ています。一部のSpring Dataモジュールは、特定のエンティティの変更を許可するストア固有のイベント( BeforeSaveEventなど)を公開します。不変の型を操作する場合など、これらのイベントは問題を引き起こす可能性があります。また、イベント発行は ApplicationEventMulticasterに依存しています。非同期 TaskExecutor を使用して構成すると、イベント処理をスレッドに分岐できるため、予測不能な結果を招く可能性があります。

エンティティコールバックは、同期ポイントとリアクティブAPIの両方を統合ポイントに提供して、処理チェーン内の明確に定義されたチェックポイントでの順序実行を保証し、潜在的に変更されたエンティティまたはリアクティブラッパータイプを返します。

エンティティコールバックは通常、APIタイプによって分離されます。この分離は、同期APIが同期エンティティコールバックのみを考慮し、リアクティブ実装がリアクティブエンティティコールバックのみを考慮することを意味します。

エンティティコールバックAPIは、Spring Data Commons 2.2で導入されました。エンティティの変更を適用する推奨方法です。既存のストア固有の ApplicationEvents は、潜在的に登録された EntityCallback インスタンスを呼び出すに引き続き公開されます。

4.8.1. エンティティコールバックの実装

EntityCallback は、ジェネリック型引数を介してドメインタイプに直接関連付けられます。通常、各Spring Dataモジュールには、エンティティのライフサイクルをカバーする一連の定義済み EntityCallback インターフェースが付属しています。

例 59: EntityCallbackの構造
@FunctionalInterface
public interface BeforeSaveCallback<T> extends EntityCallback<T> {

	/**
	 * Entity callback method invoked before a domain object is saved.
	 * Can return either the same or a modified instance.
	 *
	 * @return the domain object to be persisted.
	 */
	T onBeforeSave(T entity <2>, String collection <3>); (1)
}
1 エンティティが保存される前に呼び出されるBeforeSaveCallback 固有のメソッド。潜在的に変更されたインスタンスを返します。
2永続化する直前のエンティティ。
3エンティティが永続化されるコレクションなどのストア固有の引数の数。
例 60: リアクティブ EntityCallbackの構造
@FunctionalInterface
public interface ReactiveBeforeSaveCallback<T> extends EntityCallback<T> {

	/**
	 * Entity callback method invoked on subscription, before a domain object is saved.
	 * The returned Publisher can emit either the same or a modified instance.
	 *
	 * @return Publisher emitting the domain object to be persisted.
	 */
	Publisher<T> onBeforeSave(T entity <2>, String collection <3>); (1)
}
1 エンティティが保存される前に、サブスクリプションで呼び出されるBeforeSaveCallback 固有のメソッド。潜在的に変更されたインスタンスを発行します。
2永続化する直前のエンティティ。
3エンティティが永続化されるコレクションなどのストア固有の引数の数。
オプションのエンティティコールバックパラメータは、実装Spring Dataモジュールによって定義され、EntityCallback.callback()の呼び出しサイトから推測されます。

次の例に示すように、アプリケーションのニーズに合ったインターフェースを実装します。

例 61: 例 BeforeSaveCallback
class DefaultingEntityCallback implements BeforeSaveCallback<Person>, Ordered {      (2)

	@Override
	public Object onBeforeSave(Person entity, String collection) {                   (1)

		if(collection == "user") {
		    return // ...
		}

		return // ...
	}

	@Override
	public int getOrder() {
		return 100;                                                                  (2)
	}
}
1要件に応じたコールバックの実装。
2同じドメインタイプのエンティティコールバックが複数存在する場合、エンティティコールバックを潜在的にオーダーします。優先順位は最低です。

4.8.2. エンティティコールバックの登録

EntityCallback Beanは、ApplicationContextに登録されている場合に、ストア固有の実装によってピックアップされます。ほとんどのテンプレートAPIはすでに ApplicationContextAware を実装しているため、ApplicationContextにアクセスできます。

次の例は、有効なエンティティコールバック登録のコレクションを説明しています。

例 62: EntityCallback Bean登録の例
@Order(1)                                                           (1)
@Component
class First implements BeforeSaveCallback<Person> {

	@Override
	public Person onBeforeSave(Person person) {
		return // ...
	}
}

@Component
class DefaultingEntityCallback implements BeforeSaveCallback<Person>,
                                                           Ordered { (2)

	@Override
	public Object onBeforeSave(Person entity, String collection) {
		// ...
	}

	@Override
	public int getOrder() {
		return 100;                                                  (2)
	}
}

@Configuration
public class EntityCallbackConfiguration {

    @Bean
    BeforeSaveCallback<Person> unorderedLambdaReceiverCallback() {   (3)
        return (BeforeSaveCallback<Person>) it -> // ...
    }
}

@Component
class UserCallbacks implements BeforeConvertCallback<User>,
                                        BeforeSaveCallback<User> {   (4)

	@Override
	public Person onBeforeConvert(User user) {
		return // ...
	}

	@Override
	public Person onBeforeSave(User user) {
		return // ...
	}
}
1 BeforeSaveCallback@Order アノテーションからオーダーを受け取ります。
2 BeforeSaveCallback は、Ordered インターフェース実装を介してオーダーを受け取ります。
3 ラムダ式を使用したBeforeSaveCallback。デフォルトでは順序付けられず、最後に呼び出されます。
4単一の実装クラスに複数のエンティティコールバックインターフェースを組み合わせます。

4.8.3. 店舗固有のEntityCallbacks

Spring Data JDBCは、監査サポートに EntityCallback APIを使用し、次のコールバックに反応します。

表4: 利用可能なコールバック
EntityCallback 公開されたとき

BeforeDeleteCallback (Javadoc)

集約ルートが削除される前。

AfterDeleteCallback (Javadoc)

集約ルートが削除された後。

BeforeConvertCallback (Javadoc)

集約ルートが保存される前(つまり、挿入または更新されますが、更新または削除されるかどうかの決定が行われた後)。

BeforeSaveCallback (Javadoc)

集約ルートが保存される前(つまり、挿入または更新されますが、更新または削除されるかどうかの決定が行われた後)。

AfterSaveCallback (Javadoc)

集約ルートが保存された(つまり、挿入または更新された)後。

AfterLoadCallback (Javadoc)

データベース ResultSet から集約ルートが作成され、そのすべてのプロパティが設定された後。

4.9. ロギング

Spring Data JDBCは、それ自体でほとんどまたはまったくロギングを行いません。代わりに、SQLステートメントを発行する JdbcTemplate のメカニズムがロギングを提供します。実行されているSQLステートメントを調べたい場合は、Springの NamedParameterJdbcTemplate またはMyBatis のロギングをアクティブにします。

4.10. トランザクション性

リポジトリインスタンスのCRUDメソッドは、デフォルトではトランザクションです。読み取り操作の場合、トランザクション構成 readOnly フラグは trueに設定されます。他のすべては、デフォルトのトランザクション構成が適用されるように、プレーンな @Transactional アノテーションで構成されます。詳細については、 SimpleJdbcRepository (Javadoc) のJavadocを参照してください。リポジトリで宣言されたメソッドのいずれかのトランザクション構成を微調整する必要がある場合は、次のようにリポジトリインターフェースでメソッドを再宣言します。

例 63: CRUDのカスタムトランザクション設定
public interface UserRepository extends CrudRepository<User, Long> {

  @Override
  @Transactional(timeout = 10)
  public List<User> findAll();

  // Further query method declarations
}

上記により、findAll() メソッドは10秒のタイムアウトで readOnly フラグなしで実行されます。

トランザクションの動作を変更する別の方法は、通常複数のリポジトリをカバーするファサードまたはサービス実装を使用することです。その目的は、非CRUD操作のトランザクション境界を定義することです。次の例は、このようなファサードを作成する方法を示しています。

例 64: ファサードを使用して複数のリポジトリ呼び出しのトランザクションを定義する
@Service
class UserManagementImpl implements UserManagement {

  private final UserRepository userRepository;
  private final RoleRepository roleRepository;

  @Autowired
  public UserManagementImpl(UserRepository userRepository,
    RoleRepository roleRepository) {
    this.userRepository = userRepository;
    this.roleRepository = roleRepository;
  }

  @Transactional
  public void addRoleToAllUsers(String roleName) {

    Role role = roleRepository.findByName(roleName);

    for (User user : userRepository.findAll()) {
      user.addRole(role);
      userRepository.save(user);
    }
}

上記の例では、addRoleToAllUsers(…) の呼び出しがトランザクション内で実行されます(既存のトランザクションに参加するか、まだ実行されていない場合は新しいトランザクションを作成します)。リポジトリのトランザクション設定は無視されます。これは、外部のトランザクション設定が実際に使用されるリポジトリを決定するためです。 <tx:annotation-driven /> を明示的にアクティブにするか、@EnableTransactionManagement を使用して、ファサードが機能するようにアノテーションベースの構成を取得する必要があることに注意してください。上記の例では、コンポーネントスキャンを使用することを前提としています。

4.10.1. トランザクションクエリメソッド

クエリメソッドをトランザクション対応にするには、次の例に示すように、定義するリポジトリインターフェースで @Transactional を使用します。

例 65: クエリメソッドで@Transactionalを使用する
@Transactional(readOnly = true)
public interface UserRepository extends CrudRepository<User, Long> {

  List<User> findByLastname(String lastname);

  @Modifying
  @Transactional
  @Query("delete from User u where u.active = false")
  void deleteInactiveUsers();
}

通常、ほとんどのクエリメソッドはデータの読み取りのみを行うため、readOnly フラグをtrueに設定する必要があります。それとは対照的に、deleteInactiveUsers()@Modifying アノテーションを使用し、トランザクション構成をオーバーライドします。このメソッドは readOnly フラグが falseに設定されています。

読み取り専用クエリにトランザクションを使用することは間違いなく合理的であり、readOnly フラグを設定することでトランザクションをそのようにマークできます。ただし、これは、操作クエリをトリガーしないことのチェックとしては機能しません(ただし、一部のデータベースは読み取り専用トランザクション内で INSERT および UPDATE ステートメントを拒否します)。代わりに、readOnly フラグは、パフォーマンスの最適化のための基礎となるJDBCドライバーへのヒントとして伝搬されます。

4.11. 監査

4.11.1. 基本

Spring Dataは、エンティティの作成者または変更者と変更がいつ発生したかを透過的に追跡する高度なサポートを提供します。その機能を活用するには、アノテーションを使用するか、インターフェースを実装することで定義できる監査メタデータをエンティティクラスに装備する必要があります。

アノテーションベースの監査メタデータ

エンティティを作成または変更したユーザーをキャプチャーする @CreatedBy@LastModifiedBy、および変更が発生したときにキャプチャーする @CreatedDate@LastModifiedDate を提供します。

例 66: 監査対象エンティティ
class Customer {

  @CreatedBy
  private User user;

  @CreatedDate
  private DateTime createdDate;

  // … further properties omitted
}

ご覧のとおり、どの情報をキャプチャーするかに応じて、アノテーションを選択的に適用できます。変更が行われたときにキャプチャーするアノテーションは、タイプJoda-Time、DateTime、レガシーJava Date および Calendar、JDK8の日付と時刻タイプ、および long または Longのプロパティで使用できます。

インターフェースベースの監査メタデータ

アノテーションを使用して監査メタデータを定義したくない場合は、ドメインクラスに Auditable インターフェースを実装させることができます。すべての監査プロパティのsetterメソッドを公開します。

また、便利な基本クラス AbstractAuditableもあります。これは、インターフェースメソッドを手動で実装する必要を回避するために拡張できます。これにより、ドメインクラスのSpring Dataへの結合が増加しますが、これは避けたい場合があります。通常、監査メタデータを定義するアノテーションベースのメソッドは、侵襲性が低く、柔軟性が高いため好まれます。

AuditorAware

@CreatedBy または @LastModifiedByを使用する場合、監査インフラストラクチャは何らかの形で現在のプリンシパルを認識する必要があります。そのために、現在のユーザーまたはアプリケーションと対話するシステムが誰であるかをインフラストラクチャに伝えるために実装する必要がある AuditorAware<T> SPIインターフェースを提供します。ジェネリックタイプ T は、@CreatedBy または @LastModifiedBy アノテーションが付けられたプロパティのタイプを定義します。

次の例は、Spring Securityの Authentication オブジェクトを使用するインターフェースの実装を示しています。

例 67: Spring Securityに基づくAuditorAwareの実装
class SpringSecurityAuditorAware implements AuditorAware<User> {

  public Optional<User> getCurrentAuditor() {

    return Optional.ofNullable(SecurityContextHolder.getContext())
			  .map(SecurityContext::getAuthentication)
			  .filter(Authentication::isAuthenticated)
			  .map(Authentication::getPrincipal)
			  .map(User.class::cast);
  }
}

実装は、Spring Securityが提供する Authentication オブジェクトにアクセスし、UserDetailsService 実装で作成したカスタム UserDetails インスタンスを検索します。ここでは、UserDetails 実装を介してドメインユーザーを公開しているが、見つかった Authentication に基づいて、どこからでも検索できると想定しています。

4.12. JDBC監査

監査をアクティブにするには、次の例に示すように、@EnableJdbcAuditing を構成に追加します。

例 68: Java構成を使用した監査のアクティブ化
@Configuration
@EnableJdbcAuditing
class Config {

  @Bean
  public AuditorAware<AuditableUser> auditorProvider() {
    return new AuditorAwareImpl();
  }
}

タイプ AuditorAware のBeanを ApplicationContextに公開すると、監査インフラストラクチャはそれを自動的に選択し、それを使用してドメインタイプに設定する現在のユーザーを決定します。 ApplicationContextに複数の実装が登録されている場合、@EnableJdbcAuditingauditorAwareRef 属性を明示的に設定することにより、使用する実装を選択できます。

付録

付録A: よくある質問

ごめんなさい。今のところよくある質問はありません。

付録B: 用語集

AOP

アスペクト指向プログラミング

CRUD

作成、読み取り、更新、削除 - 基本的な永続化操作

依存性注入

外部からコンポーネントへのコンポーネントの依存関係を渡すパターン。コンポーネントを解放して、依存関係自体をルックアップします。詳細については、https://ja.wikipedia.org/wiki/依存性の注入 を参照してください。

JPA

Java Persistence API

Spring

Javaアプリケーションフレームワーク —  /projects/spring-framework

付録C: 名前空間リファレンス

<repositories /> 要素

<repositories /> 要素は、Spring Dataリポジトリインフラストラクチャのセットアップをトリガーします。最も重要な属性は base-packageです。これは、Spring Dataリポジトリインターフェースをスキャンするパッケージを定義します。「XML 構成」を参照してください。次の表は、<repositories /> 要素の属性を説明しています。

表5: 属性
名前説明

base-package

自動検出モードで *Repository を継承するリポジトリインターフェース(実際のインターフェースは特定のSpring Dataモジュールによって決定されます)をスキャンするパッケージを定義します。設定されたパッケージのすべてのパッケージもスキャンされます。ワイルドカードが許可されます。

repository-impl-postfix

カスタムリポジトリ実装を自動検出するための接尾辞を定義します。名前が構成された接尾辞で終わるクラスは、候補と見なされます。デフォルトは Implです。

query-lookup-strategy

ファインダー照会の作成に使用される戦略を決定します。詳細については、「クエリ検索戦略」を参照してください。デフォルトは create-if-not-foundです。

named-queries-location

外部で定義されたクエリを含むプロパティファイルを検索する場所を定義します。

consider-nested-repositories

ネストされたリポジトリインターフェース定義を考慮する必要があるかどうか。デフォルトは falseです。

付録D: Populators名前空間リファレンス

<populator />要素

<populator /> 要素を使用すると、Spring Dataリポジトリインフラストラクチャを介してデータストアにデータを入力できます。[1]

表6: 属性
名前説明

locations

リポジトリからオブジェクトを読み取るためのファイルの場所には、データが入力されます。

付録E: リポジトリクエリキーワード

サポートされているクエリキーワード

Spring Data JDBCはまだクエリ派生をサポートしていません。

付録F: リポジトリクエリの戻り値のタイプ

サポートされているクエリの戻り値のタイプ

次の表に、Spring Dataリポジトリで一般的にサポートされる戻り値の型を示します。ただし、ここにリストされている一部のタイプは特定のストアでサポートされていない可能性があるため、サポートされる戻り型の正確なリストについてはストア固有のドキュメントを参照してください。

地理空間タイプ( GeoResult, GeoResultsGeoPageなど)は、地理空間クエリをサポートするデータストアでのみ使用できます。
表7: クエリの戻り型
戻りの型説明

void

戻り値がないことを示します。

プリミティブ

Java プリミティブ。

ラッパーの種類

Javaラッパータイプ。

T

一意のエンティティ。クエリメソッドが最大で1つの結果を返すことを期待します。結果が見つからない場合、null が返されます。複数の結果が IncorrectResultSizeDataAccessExceptionをトリガーします。

Iterator<T>

Iterator

Collection<T>

Collection

List<T>

List

Optional<T>

Java 8またはGuava Optionalクエリメソッドが最大で1つの結果を返すことを期待します。結果が見つからない場合、Optional.empty() または Optional.absent() が返されます。複数の結果が IncorrectResultSizeDataAccessExceptionをトリガーします。

Option<T>

ScalaまたはVavr Option タイプのいずれか。前述のJava 8の Optionalと同じ意味の振る舞い。

Stream<T>

Java 8 Stream

Streamable<T>

Iterable の便利な拡張機能で、結果のストリーミング、マッピング、フィルタリング、連結などのメソッドを直接公開します。

Streamable を実装し、Streamable コンストラクターまたはファクトリーメソッド引数を取るタイプ

Streamable を引数として取るコンストラクターまたは ….of(…) / ….valueOf(…) ファクトリーメソッドを公開する型。詳細については、カスタムのストリーミング可能なラッパータイプを返すを参照してください。

Vavr Seq, List, Map, Set

Vavrコレクションタイプ。詳細については、Vavrコレクションのサポートを参照してください。

Future<T>

Futureメソッドに @Async のアノテーションが付けられることを期待し、Springの非同期メソッド実行機能を有効にする必要があります。

CompletableFuture<T>

Java 8 CompletableFutureメソッドに @Async のアノテーションが付けられることを期待し、Springの非同期メソッド実行機能を有効にする必要があります。

ListenableFuture

org.springframework.util.concurrent.ListenableFutureメソッドに @Async のアノテーションが付けられることを期待し、Springの非同期メソッド実行機能を有効にする必要があります。

Slice

使用可能なデータがさらにあるかどうかを示すサイズのデータチャンク。 Pageable メソッドパラメータが必要です。

Page<T>

結果の総数などの追加情報を含む SlicePageable メソッドパラメータが必要です。

GeoResult<T>

参照場所までの距離などの追加情報を含む結果エントリ。

GeoResults<T>

参照場所までの平均距離などの追加情報を含む GeoResult<T> のリスト。

GeoPage<T>

参照位置までの平均距離など、PageGeoResult<T>

Mono<T>

リアクティブリポジトリを使用して0個または1個の要素を放出するプロジェクトReactor Mono。クエリメソッドが最大で1つの結果を返すことを期待します。結果が見つからない場合、Mono.empty() が返されます。複数の結果が IncorrectResultSizeDataAccessExceptionをトリガーします。

Flux<T>

プロジェクトReactor Flux は、リアクティブリポジトリを使用してゼロ、1つ、または多くの要素を放出します。 Flux を返すクエリは、無限の数の要素も放出できます。

Single<T>

リアクティブリポジトリを使用して単一の要素を放出するRxJava Single。クエリメソッドが最大で1つの結果を返すことを期待します。結果が見つからない場合、Mono.empty() が返されます。複数の結果が IncorrectResultSizeDataAccessExceptionをトリガーします。

Maybe<T>

リアクティブリポジトリを使用して0個または1個の要素を放出するRxJava Maybe。クエリメソッドが最大で1つの結果を返すことを期待します。結果が見つからない場合、Mono.empty() が返されます。複数の結果が IncorrectResultSizeDataAccessExceptionをトリガーします。

Flowable<T>

リアクティブリポジトリを使用してゼロ、1つ、または多くの要素を放出するRxJava FlowableFlowable を返すクエリは、無限の数の要素も放出できます。


1. XML 構成を参照

Unofficial Translation by spring.pleiades.io. See the original content.