Aller au contenu

Semaine 4 : JOUR 4 – RELATIONS JPA, FETCH, CASCADE, DTO & INTRODUCTION À JPQL

Formation Java / Spring Boot – Niveau avancé persistance


Objectifs

À l’issue de cette journée, vous serez capable de :

Aujourd’hui, on passe au niveau “pro”…


Cours complet sur le DTO

Lien vers un cours complet sur la mise en place des DTO et les validations

Migration H2 vers PostgreSQL

Même si nous travaillons encore avec H2 (en théorie), la formation basculera vers PostgreSQL que nous avons déjà en semaine 3.

Configuration type PostgreSQL pour nos VDI :

spring.datasource.url=jdbc:postgresql://10.105.47.46:5432/db-fo-formation/bd_avions
spring.datasource.username=etude
spring.datasource.password=LeMotDePasse
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=update

PostgreSQL est :

Ce que nous faisons aujourd’hui fonctionne avec PostgreSQL.


Modélisation relationnelle : Client & Compte

Règle métier :


Implémentation JPA – côté Client

@Entity
public class Client {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String nom;

    @OneToMany(mappedBy = "client", cascade = CascadeType.ALL)
    private List<Compte> comptes = new ArrayList<>();

    public void ajouterCompte(Compte compte) {
        comptes.add(compte);
        compte.setClient(this);
    }
}

Côté Compte

@ManyToOne
@JoinColumn(name = "client_id")
private Client client;

Comprendre mappedBy

mappedBy = "client"

Signifie :

La relation est gérée par l’attribut client dans Compte. Sans mappedBy, JPA crée une table intermédiaire inutile !


Comprendre Cascade

CascadeType.ALL signifie :

si je sauvegarde un Client, ses comptes sont sauvegardés automatiquement.

Attention : le Cascade mal utilisé = suppression accidentelle


Fetch – Notion critique

Par défaut :


Exemple problème N+1

List<Client> clients = clientRepository.findAll();

Puis :

for (Client c : clients) {
    c.getComptes().size();
}

Cela déclenche :

Cela génère un problème de performance. LAZY devrait être utilisé dans ce cas.


Introduction à JPQL

JPQL = Java Persistence Query Language

Ce n’est pas du SQL, C’est du SQL orienté objet. Du coup, on interroge des entités, pas des tables !


Exemple JPQL simple

@Query("SELECT c FROM Compte c WHERE c.solde > :montant")
List<Compte> comptesRiches(@Param("montant") BigDecimal montant);

On parle de :


Pourquoi utiliser JPQL ?

Les méthodes dérivées suffisent pour :

findBySoldeGreaterThan(...)

Mais pour :

JPQL devient indispensable.


Exemple JPQL avec jointure

@Query("""
    SELECT DISTINCT c
    FROM Client c
    JOIN c.comptes comp
    WHERE comp.solde > 0
""")
List<Client> clientsAvecSoldePositif();

JPQL travaille avec :


Exemple JPQL avec agrégation

@Query("""
    SELECT c.nom, SUM(comp.solde)
    FROM Client c
    JOIN c.comptes comp
    GROUP BY c.nom
""")
List<Object[]> totalParClient();

Introduction aux DTO (Data Transfer Object)

Problème : Exposer une entité JPA directement

Solution : une DTO dédié


Exemple DTO

public record ClientDTO(
    Long id,
    String nom,
    List<String> numerosComptes
) {}

Mapper manuellement

public ClientDTO toDTO(Client client) {
    return new ClientDTO(
        client.getId(),
        client.getNom(),
        client.getComptes()
              .stream()
              .map(Compte::getNumero)
              .toList()
    );
}

Projection JPQL vers DTO

JPQL permet :

@Query("""
    SELECT new ClientSoldeDTO(c.nom, SUM(comp.solde))
    FROM Client c
    JOIN c.comptes comp
    GROUP BY c.nom
""")
List<ClientSoldeDTO> totalParClient();

Très puissant et très utilisé en entreprise.


Travaux pratiques


TP 1 – Implémenter relation Client & Compte

Consignes :

  1. Ajouter relation
  2. Tester création client + comptes
  3. Vérifier en base (console H2 / PostgreSQL)

TP 2 – Requête JPQL avec jointure

Consignes :


TP 3 – Création d’un DTO

Consignes :


TP 4 – Migration PostgreSQL


16) Erreurs fréquentes

  1. Oublier mappedBy
  2. Mauvaise gestion du cascade
  3. Utiliser EAGER partout
  4. Exposer entités directement
  5. Ignorer le problème N+1
  6. Utiliser SQL natif inutilement
  7. Retourner Object[] sans DTO
  8. Ne pas comprendre différence JPQL / SQL
  9. Ne pas tester les requêtes
  10. Ignorer la performance

Synthèse de la journée

Vous savez maintenant :


La prochaine fois on abordera

On entre dans la phase qualité professionnelle.