Formation Java / Spring Boot – Niveau avancé persistance
À l’issue de cette journée, vous serez capable de :
@OneToMany
@ManyToOne
cascade
fetch
mappedBy
Aujourd’hui, on passe au niveau “pro”…
Lien vers un cours complet sur la mise en place des DTO et les validations
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.
Règle métier :
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); } }
Compte
@ManyToOne @JoinColumn(name = "client_id") private Client client;
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 !
client
CascadeType.ALL signifie :
CascadeType.ALL
si je sauvegarde un Client, ses comptes sont sauvegardés automatiquement.
Attention : le Cascade mal utilisé = suppression accidentelle
Cascade
Par défaut :
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.
LAZY
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 !
@Query("SELECT c FROM Compte c WHERE c.solde > :montant") List<Compte> comptesRiches(@Param("montant") BigDecimal montant);
On parle de :
c.solde
Les méthodes dérivées suffisent pour :
findBySoldeGreaterThan(...)
Mais pour :
JPQL devient indispensable.
@Query(""" SELECT DISTINCT c FROM Client c JOIN c.comptes comp WHERE comp.solde > 0 """) List<Client> clientsAvecSoldePositif();
JPQL travaille avec :
@Query(""" SELECT c.nom, SUM(comp.solde) FROM Client c JOIN c.comptes comp GROUP BY c.nom """) List<Object[]> totalParClient();
Problème : Exposer une entité JPA directement
Solution : une DTO dédié
public record ClientDTO( Long id, String nom, List<String> numerosComptes ) {}
public ClientDTO toDTO(Client client) { return new ClientDTO( client.getId(), client.getNom(), client.getComptes() .stream() .map(Compte::getNumero) .toList() ); }
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.
Consignes :
ClientSoldeDTO
Vous savez maintenant :
@ControllerAdvice
On entre dans la phase qualité professionnelle.