Référentiels composables de données Spring

1. Introduction

Lors de la modélisation d'un système ou d'un processus du monde réel, les référentiels de style DDD (domain-driven design) sont une bonne option. Dans ce but précis, nous pouvons utiliser Spring Data JPA comme couche d'abstraction d'accès aux données.

Si vous êtes nouveau dans ce concept, consultez ce didacticiel d'introduction pour vous aider à vous mettre à niveau.

Dans ce didacticiel, nous allons nous concentrer sur le concept de création de référentiels personnalisés et composables qui sont créés à l'aide de référentiels plus petits appelés fragments.

2. Dépendances de Maven

L'option de création de référentiels composables est disponible à partir de Spring 5.

Ajoutons la dépendance requise pour Spring Data JPA:

 org.springframework.data spring-data-jpa 2.2.2.RELEASE 

Nous aurions également besoin de configurer une source de données pour que notre couche d'accès aux données fonctionne. C'est une bonne idée de configurer une base de données en mémoire comme H2 pour le développement et les tests rapides.

3. Contexte

3.1. Hibernate en tant qu'implémentation JPA

Spring Data JPA, par défaut, utilise Hibernate comme implémentation JPA. On peut facilement les confondre ou les comparer, mais ils servent des objectifs différents.

Spring Data JPA est la couche d'abstraction d'accès aux données sous laquelle nous pouvons utiliser n'importe quelle implémentation. Nous pourrions, par exemple, changer Hibernate au profit d'EclipseLink.

3.2. Référentiels par défaut

Dans de nombreux cas, nous n'avons pas besoin d'écrire nous-mêmes des requêtes.

Au lieu de cela, nous devons seulement créer des interfaces qui à leur tour étendent les interfaces génériques du référentiel de données Spring:

public interface LocationRepository extends JpaRepository { }

Et cela, en soi, nous permettrait de faire des opérations communes - CRUD, pagination et tri - sur l' objet Location qui a une clé primaire de type Long .

De plus, Spring Data JPA est équipé d'un mécanisme de générateur de requêtes qui offre la possibilité de générer des requêtes en notre nom en utilisant les conventions de nom de méthode:

public interface StoreRepository extends JpaRepository { List findStoreByLocationId(Long locationId); }

3.3. Référentiels personnalisés

Si nécessaire, nous pouvons enrichir notre référentiel de modèles en écrivant une interface de fragment et en implémentant la fonctionnalité souhaitée. Cela peut ensuite être injecté dans notre propre référentiel JPA.

Par exemple, nous enrichissons ici notre ItemTypeRepository en étendant un référentiel de fragments:

public interface ItemTypeRepository extends JpaRepository, CustomItemTypeRepository { }

Ici, CustomItemTypeRepository est une autre interface:

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

Son implémentation peut être un référentiel de n'importe quel type, pas seulement JPA:

public class CustomItemTypeRepositoryImpl implements CustomItemTypeRepository { @Autowired private EntityManager entityManager; @Override public void deleteCustomById(ItemType itemType) { entityManager.remove(itemType); } }

Nous devons juste nous assurer qu'il a le suffixe Impl . Cependant, nous pouvons définir un suffixe personnalisé en utilisant la configuration XML suivante:

ou en utilisant cette annotation:

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

4. Composition de référentiels à l'aide de plusieurs fragments

Jusqu'à il y a quelques versions, nous ne pouvions étendre nos interfaces de référentiel qu'en utilisant une seule implémentation personnalisée. C'était une limitation à cause de laquelle nous devions rassembler toutes les fonctionnalités associées dans un seul objet.

Inutile de dire que pour des projets plus importants avec des modèles de domaine complexes, cela conduit à des classes gonflées.

Désormais, avec Spring 5, nous avons la possibilité d' enrichir notre référentiel JPA avec plusieurs référentiels de fragments . Encore une fois, l'exigence demeure que nous ayons ces fragments sous forme de paires d'interface-implémentation.

Pour le démontrer, créons deux fragments:

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

Bien sûr, nous aurions besoin d'écrire leurs implémentations. Mais au lieu de brancher ces référentiels personnalisés - avec des fonctionnalités associées - dans leurs propres référentiels JPA, nous pouvons étendre les fonctionnalités d'un référentiel JPA unique:

public interface ItemTypeRepository extends JpaRepository, CustomItemTypeRepository, CustomItemRepository { }

Maintenant, nous aurions toutes les fonctionnalités liées dans un seul référentiel.

5. Gérer l'ambiguïté

Puisque nous héritons de plusieurs référentiels, nous pouvons avoir du mal à déterminer laquelle de nos implémentations serait utilisée en cas de conflit. Par exemple, dans notre exemple, les deux référentiels de fragments ont une méthode, findThenDelete () , avec la même signature.

Dans ce scénario, l'ordre de la déclaration des interfaces est utilisé pour résoudre l'ambiguïté . Par conséquent, dans notre cas, la méthode à l'intérieur de CustomItemTypeRepository sera utilisée puisqu'elle est déclarée en premier.

Nous pouvons tester cela en utilisant ce cas de test:

@Test public void givenItemAndItemTypeWhenDeleteThenItemTypeDeleted() { Optional itemType = composedRepository.findById(1L); assertTrue(itemType.isPresent()); Item item = composedRepository.findItemById(2L); assertNotNull(item); composedRepository.findThenDelete(1L); Optional sameItemType = composedRepository.findById(1L); assertFalse(sameItemType.isPresent()); Item sameItem = composedRepository.findItemById(2L); assertNotNull(sameItem); }

6. Conclusion

Dans cet article, nous avons examiné les différentes façons dont nous pouvons utiliser les référentiels Spring Data JPA. Nous avons vu que Spring simplifie l'exécution d'opérations de base de données sur nos objets de domaine sans écrire beaucoup de code ni même de requêtes SQL.

Ce support est considérablement personnalisable grâce à l'utilisation de référentiels composables.

Les extraits de code de cet article sont disponibles en tant que projet Maven ici sur GitHub.