Spring Data Composable Repository

データ]

  • リンク:/tag/jpa/[JPA]

1.はじめに

実社会のシステムまたはプロセスをモデル化するときは、ドメイン駆動設計(DDD)スタイルのリポジトリーが優れた選択肢です。この目的のために、Spring Data JPAをデータアクセス抽象化レイヤとして使用できます。

この概念に慣れていない場合は、https://www.baeldung.com/the-persistence-layer-with-spring-data-jpa[この入門チュートリアル]をチェックして、スピードアップしてください。

このチュートリアルでは、フラグメントと呼ばれる小さなリポジトリを使用して作成されるカスタムリポジトリと作成可能リポジトリの作成の概念に焦点を当てます。

2. Mavenの依存関係

構成可能なリポジトリを作成するためのオプションは、Spring 5以降で利用可能です。

Spring Data JPAに必要な依存関係を追加しましょう。

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jpa</artifactId>
    <version>2.0.9.RELEASE</version>
</dependency>

データアクセスレイヤを機能させるには、データソースを設定する必要もあります。開発と素早いテストのためにhttps://www.baeldung.com/spring-testing-separate-data-source[H2のようなインメモリデータベースをセットアップする]ことをお勧めします。

3.背景

3.1. JPA実装としてのHibernate

Spring Data JPAは、デフォルトでJPAの実装としてHibernateを使用します。

一方を他方と混同したり、比較したりすることは簡単にできますが、目的は異なります。

Spring Data JPAはデータアクセス抽象化レイヤであり、その下にある任意の実装を使用できます。例えば、https://www.baeldung.com/spring-eclipselink[EclipseLinkを支持してHibernateを切り換える]などです。

3.2. デフォルトのリポジトリ

多くの場合、クエリを自分で作成する必要はありません。

代わりに、Springの一般的なデータリポジトリインターフェースを拡張するインターフェースを作成するだけです。

public interface LocationRepository extends JpaRepository<Location, Long> {
}

そしてこれは、それ自体では、 Long 型の主キーを持つ Location オブジェクトに対して共通の操作(CRUD、ページング、ソート)を実行することを可能にします。

さらに、Spring Data JPAは、メソッドの命名規則を使用して私たちの代わりにクエリを生成する機能を提供するクエリビルダーメカニズムを備えています。

public interface StoreRepository extends JpaRepository<Store, Long> {
    List<Store> findStoreByLocationId(Long locationId);
}

3.3. カスタムリポジトリ

必要に応じて、フラグメントインタフェースを作成して目的の機能を実装することで、モデルリポジトリを充実させることができます。その後、これを私たち自身のJPAリポジトリに注入することができます。

たとえば、ここではフラグメントリポジトリを拡張することで ItemTypeRepository を強化しています。

public interface ItemTypeRepository
  extends JpaRepository<ItemType, Long>, CustomItemTypeRepository {
}

ここで CustomItemTypeRepository は別のインターフェースです。

public interface CustomItemTypeRepository {
    void deleteCustomById(ItemType entity);
}

その実装はJPAだけでなく、あらゆる種類のリポジトリにすることができます。

public class CustomItemTypeRepositoryImpl implements CustomItemTypeRepository {

    @Autowired
    private EntityManager entityManager;

    @Override
    public void deleteCustomById(ItemType itemType) {
        entityManager.remove(itemType);
    }
}

それが後置 Impl を持っていることを確認する必要があります。ただし、次のXML構成を使用してカスタムの後置を設定できます。

<repositories base-package="com.baeldung.repository" repository-impl-postfix="CustomImpl"/>

またはこのアノテーションを使用して:

@EnableJpaRepositories(
  basePackages = "com.baeldung.repository", repositoryImplementationPostfix = "CustomImpl")

4.複数のフラグメントを使ってリポジトリを作成する

数リリース前までは、単一のカスタム実装を使用してリポジトリインタフェースを拡張することしかできませんでした。これは制限事項でした。そのため、すべての関連機能を1つのオブジェクトにまとめなければなりませんでした。

言うまでもありませんが、複雑なドメインモデルを持つ大規模プロジェクトでは、クラスが肥大化します。

Spring 5では、JPAリポジトリに複数のフラグメントリポジトリを追加することができます。繰り返しになりますが、これらのフラグメントをインターフェースと実装のペアとして持つという要件は残っています。

これを実証するために、2つのフラグメントを作成しましょう。

public interface CustomItemTypeRepository {
    void deleteCustom(ItemType entity);
    void findThenDelete(Long id);
}

public interface CustomItemRepository {
    Item findItemById(Long id);
    void deleteCustom(Item entity);
    void findThenDelete(Long id);
}

もちろん、それらの実装を書く必要があります。しかし、これらのカスタムリポジトリを(関連する機能とともに)独自のJPAリポジトリにプラグインする代わりに、単一のJPAリポジトリの機能を拡張することができます。

public interface ItemTypeRepository
  extends JpaRepository<ItemType, Long>, CustomItemTypeRepository, CustomItemRepository {
}

これで、リンクされたすべての機能を1つの単一のリポジトリにまとめました。

5.あいまいさに対処する

私たちは複数のリポジトリから継承しているので、衝突の場合にどの実装が使用されるのかを理解するのに苦労するかもしれません。たとえば、この例では、両方のフラグメントリポジトリに同じシグネチャを持つメソッド findThenDelete() があります。

このシナリオでは、** インターフェースの宣言の順序はあいまいさを解決するために使用されます。そのため、今回のケースでは、最初に宣言されているため、 CustomItemTypeRepository 内のメソッドが使用されます。

このテストケースを使用してこれをテストできます。

@Test
public void givenItemAndItemTypeWhenDeleteThenItemTypeDeleted() {
    Optional<ItemType> itemType = composedRepository.findById(1L);
    assertTrue(itemType.isPresent());

    Item item = composedRepository.findItemById(2L);
    assertNotNull(item);

    composedRepository.findThenDelete(1L);
    Optional<ItemType> sameItemType = composedRepository.findById(1L);
    assertFalse(sameItemType.isPresent());

    Item sameItem = composedRepository.findItemById(2L);
    assertNotNull(sameItem);
}

6.まとめ

この記事では、Spring Data JPAリポジトリーを使用できるさまざまな方法について説明しました。 Springでは、コードやSQLクエリをあまり書かずに、ドメインオブジェクトに対してデータベース操作を簡単に実行できることがわかりました。

このサポートは構成可能なリポジトリの使用を通してかなりカスタマイズ可能です。

この記事のコードスニペットはhttps://github.com/eugenp/tutorials/tree/master/persistence-modules/spring-data-jpa[ここのGithubでのプロジェクト]として入手できます。