Dépôts de données composables Spring

Dépôts de données composables Spring

1. introduction

Lorsque vous modélisez un système ou un processus 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 en tant que couche d'abstraction d'accès aux données.

Si vous êtes nouveau dans ce concept, consultezthis introductory tutorial pour vous aider à vous mettre à niveau.

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

2. Dépendances Maven

L'option pour créer des 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.0.9.RELEASE

Nous devrons également configurer une source de données pour que notre couche d'accès aux données fonctionne. C'est une bonne idée deset up an in-memory database like H2 pour le développement et les tests rapides.

3. Contexte

3.1. Hibernate comme implémentation de JPA

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

Spring Data JPA est la couche d’abstraction d’accès aux données en dessous de laquelle on peut utiliser n’importe quelle implémentation. Nous pourrions, par exemple,switch out Hibernate in favor of EclipseLink.

3.2. Dépôts par défaut

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

Au lieu de cela, nous avons seulement besoin de créer des interfaces qui étendent à leur tour 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 courantes - CRUD, pagination et tri - sur l'objetLocation qui a une clé primaire de typeLong.

En outre, Spring Data JPA est équipé d’un mécanisme de création de requêtes qui permet de générer des requêtes pour notre compte à l’aide de conventions de noms de méthodes:

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

3.3. Dépôts personnalisés

Si nécessaire, nous pouvonsenrich our model repository en écrivant une interface fragmentée et en implémentant la fonctionnalité souhaitée. Ceci peut ensuite être injecté dans notre propre référentiel JPA.

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

public interface ItemTypeRepository
  extends JpaRepository, CustomItemTypeRepository {
}

IciCustomItemTypeRepository est une autre interface:

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

Son implémentation peut être un référentiel de tout 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 suffixeImpl. Cependant, nous pouvons définir un postfix personnalisé en utilisant la configuration XML suivante:

ou en utilisant cette annotation:

@EnableJpaRepositories(
  basePackages = "com.example.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'à l'aide d'une seule implémentation personnalisée. C'était une limitation en raison de laquelle nous devions regrouper toutes les fonctionnalités associées dans un seul objet.

Il va sans dire que, pour les projets plus importants avec des modèles de domaine complexes, cela conduit à des classes surchargées.

Maintenant, avec Spring 5, nous avons la possibilité deenrich our JPA repository with multiple fragment repositories. Là encore, l'exigence reste que nous ayons ces fragments sous forme de paires interface-implémentation.

Pour illustrer cela, 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 devrons écrire leurs implémentations. Mais au lieu de brancher ces référentiels personnalisés - avec des fonctionnalités connexes - 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 {
}

Désormais, nous avons toutes les fonctionnalités liées dans un seul référentiel.

5. Faire face à l'ambiguïté

Étant donné que nous héritons de plusieurs référentiels, il se peut que nous ayons 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,the order of the declaration of the interfaces is used to resolve the ambiguity. Par conséquent, dans notre cas, la méthode à l'intérieur deCustomItemTypeRepository 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é différentes manières d'utiliser les référentiels Spring Data JPA. Nous avons vu que Spring simplifie les opérations de base de données sur nos objets de domaine sans écrire beaucoup de code ni même de requêtes SQL.

Cette prise en charge est considérablement personnalisable grâce à l’utilisation de référentiels composables.

Les extraits de code de cet article sont disponibles sous forme deMaven project here on Github.