Contactez-nous

Création d'interfaces Repository (`JpaRepository`, `CrudRepository`)

Apprenez à définir vos interfaces d'accès aux données en étendant CrudRepository et JpaRepository dans Spring Data JPA pour bénéficier des opérations CRUD et plus.

Définir le contrat d'accès aux données

Au coeur de Spring Data se trouve le concept de Repository, une interface qui définit les opérations d'accès aux données pour une entité spécifique. L'étape fondamentale pour utiliser Spring Data JPA est donc de créer ces interfaces pour vos entités. Vous n'avez pas besoin d'écrire l'implémentation ; Spring Data s'en charge pour vous.

Pour ce faire, vous créez une interface Java qui étend une des interfaces de base fournies par Spring Data. Les choix les plus courants pour JPA sont CrudRepository et JpaRepository, chacune offrant un niveau de fonctionnalité différent.

`CrudRepository` : Les bases du CRUD

L'interface org.springframework.data.repository.CrudRepository est l'une des interfaces fondamentales. Elle fournit un ensemble standard de méthodes pour les opérations CRUD (Create, Read, Update, Delete).

En étendant CrudRepository, votre interface personnalisée hérite automatiquement des méthodes suivantes :

  • S save(S entity): Sauvegarde (insère ou met à jour) une entité donnée.
  • Iterable saveAll(Iterable entities): Sauvegarde plusieurs entités.
  • Optional findById(ID id): Récupère une entité par son identifiant. Retourne un Optional pour gérer élégamment le cas où l'entité n'est pas trouvée.
  • boolean existsById(ID id): Vérifie si une entité avec l'ID donné existe.
  • Iterable findAll(): Récupère toutes les entités.
  • Iterable findAllById(Iterable ids): Récupère toutes les entités pour les IDs donnés.
  • long count(): Compte le nombre total d'entités.
  • void deleteById(ID id): Supprime l'entité avec l'ID donné.
  • void delete(T entity): Supprime une entité donnée.
  • void deleteAllById(Iterable ids): Supprime les entités pour les IDs donnés.
  • void deleteAll(Iterable entities): Supprime les entités données.
  • void deleteAll(): Supprime toutes les entités du repository (à utiliser avec précaution!).

Exemple :

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository // Annotation recommandée pour la traduction d'exceptions
public interface BasicProductRepository extends CrudRepository {
    // Pas besoin d'ajouter les méthodes CRUD ici, elles sont héritées.
    // On peut ajouter des méthodes de requête dérivées si nécessaire.
    List findByName(String name);
}

// Entité Product (simplifiée)
@Entity
class Product {
    @Id @GeneratedValue
    private Long id;
    private String name;
    // ...
}

CrudRepository est un bon choix si vous n'avez besoin que des opérations CRUD de base et éventuellement de quelques requêtes dérivées simples.

`JpaRepository` : Le choix complet pour JPA

L'interface org.springframework.data.jpa.repository.JpaRepository est l'interface la plus couramment utilisée et recommandée pour les projets basés sur JPA. Elle étend PagingAndSortingRepository (qui elle-même étend CrudRepository), ce qui signifie qu'elle hérite de toutes les méthodes CRUD ainsi que des méthodes de pagination et de tri.

En plus de cela, JpaRepository ajoute des méthodes spécifiques à JPA qui tirent parti des fonctionnalités de l'EntityManager sous-jacent :

  • List findAll(): Surcharge de CrudRepository pour retourner une List au lieu d'un Iterable.
  • List findAll(Sort sort): Récupère toutes les entités, triées.
  • List findAllById(Iterable ids): Surcharge pour retourner une List.
  • List saveAll(Iterable entities): Surcharge pour retourner une List.
  • void flush(): Synchronise le contexte de persistance avec la base de données sous-jacente (force l'exécution des instructions SQL en attente).
  • S saveAndFlush(S entity): Sauvegarde une entité et appelle flush() immédiatement après.
  • void deleteInBatch(Iterable entities) (Déprécié, préférer deleteAllInBatch): Supprime les entités données en utilisant une seule requête optimisée si possible (plus efficace que des suppressions individuelles).
  • void deleteAllInBatch(Iterable entities): Remplace deleteInBatch.
  • void deleteAllByIdInBatch(Iterable ids): Supprime par ID en utilisant une seule requête optimisée.
  • T getOne(ID id) (Déprécié): Retourne une référence proxy à l'entité sans nécessairement la charger depuis la base. Utile pour établir des relations. Peut lever EntityNotFoundException si l'entité n'existe pas lors de l'accès aux propriétés.
  • T getById(ID id): Remplace getOne avec un comportement similaire mais une sémantique potentiellement plus claire selon les versions de JPA/Hibernate.
  • List findAll(Example example): Support pour les requêtes par l'exemple (Query by Example).
  • List findAll(Example example, Sort sort): Requêtes par l'exemple avec tri.

Exemple :

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;

@Repository
public interface OrderRepository extends JpaRepository {
    // Hérite CRUD, Pagination/Tri, et méthodes spécifiques JPA
    
    // Exemple de requête dérivée
    List findByCustomerNameOrderByOrderDateDesc(String customerName);
}

// Entité Order (simplifiée)
@Entity
@Table(name = "orders") // Bonne pratique de nommer la table
class Order {
    @Id @GeneratedValue
    private Long id;
    private String customerName;
    private java.time.LocalDateTime orderDate;
    // ...
}

Etant donné qu'elle offre l'ensemble de fonctionnalités le plus complet (CRUD, pagination, tri, opérations spécifiques à JPA), JpaRepository est généralement le meilleur point de départ pour la plupart des applications utilisant Spring Data JPA.

Comment utiliser ces interfaces ?

Une fois que vous avez défini votre interface Repository (par exemple, OrderRepository), vous n'avez rien de plus à faire au niveau de l'implémentation. Spring Data détecte votre interface au démarrage et crée automatiquement un bean (un proxy) qui implémente cette interface.

Vous pouvez ensuite injecter cette interface directement dans vos composants (comme les services ou les contrôleurs) en utilisant l'injection de dépendances (@Autowired) et appeler ses méthodes comme si une implémentation concrète existait.

import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Optional;

@Service
public class OrderService {

    private final OrderRepository orderRepository; // Injection de l'interface

    @Autowired
    public OrderService(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }

    @Transactional(readOnly = true) // Bonne pratique pour les méthodes de lecture
    public Optional getOrderById(Long id) {
        return orderRepository.findById(id); // Appel direct d'une méthode héritée
    }

    @Transactional(readOnly = true)
    public List findOrdersForCustomer(String customerName) {
        // Appel direct d'une méthode de requête dérivée
        return orderRepository.findByCustomerNameOrderByOrderDateDesc(customerName);
    }

    @Transactional // Pas readOnly car modifie des données
    public Order createOrder(Order order) {
        return orderRepository.save(order); // Appel direct d'une méthode héritée
    }
    
    // ... autres méthodes métier
}

Le rôle de l'annotation `@Repository`

Vous avez peut-être remarqué l'annotation @Repository sur les exemples d'interfaces. Quel est son rôle exact ?

  1. Détection par Component Scan : Elle marque l'interface comme un bean candidat à la détection par le scan des composants de Spring. Cependant, si votre interface étend Repository ou l'une de ses sous-interfaces, Spring Data la détectera même sans cette annotation, à condition qu'elle se trouve dans un package scanné.
  2. Traduction d'exceptions : C'est la raison principale et la plus importante d'utiliser @Repository avec Spring Data JPA. Cette annotation active un post-processeur de bean qui intercepte les exceptions spécifiques à la plateforme de persistance (comme PersistenceException de JPA ou SQLException de JDBC) levées par le repository et les traduit en exceptions non-cochées standard de la hiérarchie DataAccessException de Spring. Cela permet de découpler votre code métier des détails de la technologie de persistance et facilite une gestion cohérente des erreurs d'accès aux données.

Conclusion : Même si elle n'est pas strictement obligatoire pour la détection des interfaces Repository de Spring Data, il est fortement recommandé d'annoter vos interfaces Repository avec @Repository pour bénéficier de la traduction automatique des exceptions.