SpringDataコンポーザブルリポジトリ
1. 序章
実際のシステムまたはプロセスをモデル化する場合、ドメイン駆動設計(DDD)スタイルのリポジトリが適切なオプションです。 この目的のために、SpringDataJPAをデータアクセス抽象化レイヤーとして使用できます。
この概念に慣れていない場合は、この入門チュートリアルをチェックして、理解を深めてください。
このチュートリアルでは、フラグメントと呼ばれる小さなリポジトリを使用して作成されるカスタムリポジトリとコンポーザブルリポジトリを作成するという概念に焦点を当てます。
2. Mavenの依存関係
構成可能なリポジトリを作成するオプションは、Spring5以降で利用できます。
SpringDataJPAに必要な依存関係を追加しましょう。
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
また、データアクセス層が機能するためには、データソースを設定する必要があります。 開発と迅速なテストのために、H2のようなインメモリデータベースをセットアップすることをお勧めします。
3. バックグラウンド
3.1. JPA実装として休止
Spring Data JPAは、デフォルトで、JPA実装としてHibernateを使用します。 簡単に混同したり比較したりできますが、目的は異なります。
Spring Data JPAは、データアクセス抽象化レイヤーであり、その下で任意の実装を使用できます。 たとえば、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. 結論
この記事では、SpringDataJPAリポジトリーを使用するさまざまな方法について説明しました。 Springを使用すると、多くのコードやSQLクエリを記述しなくても、ドメインオブジェクトに対してデータベース操作を簡単に実行できることがわかりました。
このサポートは、構成可能なリポジトリを使用して大幅にカスタマイズできます。
この記事のコードスニペットは、GitHubでMavenプロジェクトとして入手できます。