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

リポジトリインターフェースを定義するには、最初にドメインクラス固有のリポジトリインターフェースを定義する必要があります。インターフェースは Repository を継承し、ドメインクラスと ID 型に入力する必要があります。そのドメイン型の CRUD メソッドを公開する場合は、CrudRepository、または Repository の代わりにそのバリアントの 1 つを継承できます。

リポジトリ定義の微調整

リポジトリインターフェースを使い始める方法にはいくつかのバリエーションがあります。

典型的なアプローチは、CrudRepository を継承することです。これにより、CRUD 機能のメソッドが提供されます。CRUD は、Create、Read、Update、Delete の略です。バージョン 3.0 では、CrudRepository と非常によく似た ListCrudRepository も導入されましたが、複数のエンティティを返すメソッドでは、使いやすい Iterable ではなく List が返されます。

リアクティブストアを使用している場合は、使用しているリアクティブフレームワークに応じて ReactiveCrudRepository または RxJava3CrudRepository を選択できます。

Kotlin を使用している場合は、Kotlin のコルーチンを利用する CoroutineCrudRepository を選択できます。

さらに、Sort 抽象化または前者の場合は Pageable 抽象化を指定できるメソッドが必要な場合は、PagingAndSortingRepositoryReactiveSortingRepositoryRxJava3SortingRepository または CoroutineSortingRepository を継承できます。さまざまなソートリポジトリは、3.0 より前の Spring Data バージョンのように、それぞれの CRUD リポジトリを継承しなくなったことに注意してください。両方の機能が必要な場合は、両方のインターフェースを継承する必要があります。

Spring Data インターフェースを継承したくない場合は、リポジトリインターフェースに @RepositoryDefinition でアノテーションを付けることもできます。CRUD リポジトリインターフェースの 1 つを継承すると、エンティティを操作するためのメソッドの完全なセットが公開されます。公開するメソッドを選択したい場合は、公開するメソッドを CRUD リポジトリからドメインリポジトリにコピーします。その際、メソッドの戻り値の型を変更できます。Spring Data は、可能であれば戻り値の型を尊重します。例: 複数のエンティティを返すメソッドの場合、Iterable<T>List<T>Collection<T> または VAVR リストを選択できます。

アプリケーション内の多くのリポジトリに同じメソッドのセットが必要な場合は、継承元の独自のベースインターフェースを定義できます。このようなインターフェースには、@NoRepositoryBean のアノテーションを付ける必要があります。これにより、Spring Data はインスタンスを直接作成しようとして失敗し、そのリポジトリのエンティティを特定できないために失敗します。これは、ジェネリクス型変数がまだ含まれているためです。

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

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 が実行時にインスタンスを作成してはならないすべてのリポジトリインターフェースに、そのアノテーションを必ず追加してください。

複数の 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)を使用するリポジトリを示しています。

例 1: モジュール固有のインターフェースを使用したリポジトリ定義
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 モジュールの有効な候補です。

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

例 2: ジェネリクスインターフェースを使用したリポジトリ定義
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 にバインドする必要があるかを区別できません。

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

例 3: アノテーション付きのドメインクラスを使用したリポジトリ定義
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 を参照します。

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

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

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

@Entity
@Document
class Person { … }

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

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

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

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

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