カスタムリポジトリの実装

Spring Data は、コーディングをほとんど行わずにクエリメソッドを作成するためのさまざまなオプションを提供します。ただし、これらのオプションがニーズに合わない場合は、リポジトリメソッドの独自のカスタム実装を提供することもできます。このセクションでは、その方法について説明します。

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

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

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

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

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

次に、次のように、リポジトリインターフェースにフラグメントインターフェースを継承させることができます。

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

  // Declare query methods here
}

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

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

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

実装のフラグメント
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 を継承するカスタムリポジトリのインターフェースを示しています。

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

  // Declare query methods here
}

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

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

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

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

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

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

構成

リポジトリインフラストラクチャは、リポジトリが見つかったパッケージのクラスをスキャンして、カスタム実装フラグメントを自動検出しようとします。これらのクラスは、デフォルトで Impl になる接尾辞を追加するという命名規則に従う必要があります。

次の例は、デフォルトの接尾辞を使用するリポジトリと、接尾辞のカスタム値を設定するリポジトリを示しています。

例 1: 構成例
  • Java

  • XML

@EnableJdbcRepositories(repositoryImplementationPostfix = "MyPostfix")
class Configuration { … }
<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 に一致します。

例 2: あいまいな実装の解決
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 定義を手動で作成する代わりに、名前で手動で定義したものを参照します。次の例は、カスタム実装を手動で接続する方法を示しています。

例 3: カスタム実装の手動接続
  • Java

  • XML

class MyClass {
  MyClass(@Qualifier("userRepositoryImpl") UserRepository userRepository) {
    …
  }
}
<repositories base-package="com.acme.repository" />

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

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

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

カスタムリポジトリベースクラス
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 インフラストラクチャに認識させることです。構成では、次の例に示すように、repositoryBaseClass を使用してこれを行うことができます。

例 4: カスタムリポジトリ基本クラスの構成
  • Java

  • XML

@Configuration
@EnableJdbcRepositories(repositoryBaseClass = MyRepositoryImpl.class)
class ApplicationConfiguration { … }
<repositories base-package="com.acme.repository"
     base-class="….MyRepositoryImpl" />