CHAPITRE 7 : LES DONNEES AVEC SPRING DATA JPA

Jusqu'ici nos donnees disparaissaient a chaque redemarrage. Nous allons les enregistrer durablement.

1. Ajouter les dependances

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

H2 est une base qui fonctionne en memoire : parfaite pour apprendre, aucune installation. Nous passerons a MySQL plus loin.

2. Configurer la base

# H2 en memoire
spring.datasource.url=jdbc:h2:mem:ecole
spring.h2.console.enabled=true

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

La console H2 devient accessible sur http://localhost:8080/h2-console : tres pratique pour voir ce qui est reellement enregistre.

L'option ddl-auto

ValeurEffet
updateCree et complete les tables sans effacer
createRecree les tables a chaque demarrage
create-dropComme create, et supprime a l'arret
validateVerifie seulement la correspondance
noneNe touche a rien

Avertissement : ne jamais utiliser create ni create-drop en production : vos donnees seraient effacees a chaque redemarrage. En production, on utilise validate ou none.

3. L'entite

@Entity
@Table(name = "etudiants")
public class Etudiant {

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

    @Column(nullable = false, length = 80)
    private String nom;

    @Column(unique = true, nullable = false)
    private String matricule;

    private double moyenne;

    protected Etudiant() { }   // exige par JPA

    public Etudiant(String nom, String matricule, double moyenne) {
        this.nom = nom;
        this.matricule = matricule;
        this.moyenne = moyenne;
    }

    // getters et setters
}
AnnotationRole
@EntityCette classe correspond a une table
@TableNom de la table (sinon, celui de la classe)
@IdLa cle primaire
@GeneratedValueValeur generee automatiquement
@ColumnContraintes de la colonne

Le constructeur sans argument est obligatoire pour JPA, meme si vous ne l'utilisez jamais. Le declarer protected evite qu'on s'en serve par erreur.

4. Le repository

public interface EtudiantRepository extends JpaRepository<Etudiant, Long> {
}

Une interface vide, et vous disposez deja de :

findAll()           save(entite)        findById(id)
deleteById(id)      count()             existsById(id)

Spring Data genere l'implementation au demarrage. Vous n'ecrivez aucune ligne de SQL.

5. Les requetes derivees du nom

C'est la fonctionnalite la plus surprenante : il suffit de nommer la methode.

public interface EtudiantRepository extends JpaRepository<Etudiant, Long> {

    List<Etudiant> findByNom(String nom);

    List<Etudiant> findByMoyenneGreaterThan(double seuil);

    List<Etudiant> findByNomContainingIgnoreCase(String fragment);

    Optional<Etudiant> findByMatricule(String matricule);

    List<Etudiant> findByMoyenneBetweenOrderByMoyenneDesc(double min, double max);

    long countByMoyenneGreaterThanEqual(double seuil);
}
Mot-cleEquivalent SQL
GreaterThan>
BetweenBETWEEN
ContainingLIKE %…%
IgnoreCaseInsensible a la casse
OrderBy…DescORDER BY … DESC
And / OrAND / OR

6. Ecrire sa propre requete

@Query("SELECT e FROM Etudiant e WHERE e.moyenne >= :seuil ORDER BY e.moyenne DESC")
List<Etudiant> trouverAdmis(@Param("seuil") double seuil);

@Query(value = "SELECT * FROM etudiants WHERE moyenne > ?1", nativeQuery = true)
List<Etudiant> trouverAvecSqlNatif(double seuil);

JPQL porte sur les entites, pas sur les tables : on ecrit Etudiant avec une majuscule, et e.moyenne, non le nom de la colonne.

7. Le service

@Service
@Transactional(readOnly = true)
public class EtudiantService {

    private final EtudiantRepository repository;

    public EtudiantService(EtudiantRepository repository) {
        this.repository = repository;
    }

    public List<Etudiant> listerTous() {
        return repository.findAll();
    }

    @Transactional
    public Etudiant enregistrer(Etudiant e) {
        return repository.save(e);
    }

    @Transactional
    public void supprimer(Long id) {
        repository.deleteById(id);
    }
}

@Transactional garantit qu'une operation est entierement validee ou entierement annulee. Le readOnly = true au niveau de la classe optimise les lectures ; on le surcharge sur les methodes qui ecrivent.

8. Passer a MySQL

spring.datasource.url=jdbc:mysql://localhost:3306/ecole?createDatabaseIfNotExist=true
spring.datasource.username=root
spring.datasource.password=votre_mot_de_passe
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect

Avec la dependance correspondante dans pom.xml. Le reste du code ne change pas d'une ligne : c'est tout l'interet de JPA.

A retenir

  • @Entity relie une classe a une table
  • Une interface JpaRepository vide fournit deja le CRUD
  • Le nom de la methode suffit a generer la requete
  • ddl-auto=create efface tout : jamais en production

Exercices d'application

Exercice 1. Persister les etudiantsTransformez votre Etudiant en entite JPA, creez le repository et verifiez dans la console H2 que les donnees sont bien enregistrees.
Exercice 2. Requetes deriveesAjoutez trois methodes : chercher par matricule, lister ceux dont la moyenne depasse un seuil, et compter les admis. Testez chacune.
Exercice 3. Changer de basePassez de H2 a MySQL en ne modifiant que application.properties et pom.xml. Verifiez que le code Java reste inchange.

Cherchez avant de regarder la solution : c'est en butant sur les difficultes que l'on progresse.

Questions / Réponses

Aucune question. Soyez le premier à poser une question.
★ ★ ★ ★ ★
2 votes. Moyenne 5 sur 5.

Ajouter un commentaire

Anti-spam