Enregistrer des données
Notions théoriques
Le repository : interface entre Java et la base de données
En Spring Boot, on ne manipule pas directement SQL pour sauvegarder des objets. On utilise un repository, une interface qui hérite de JpaRepository. Spring génère automatiquement l'implémentation.
| Doctrine (Symfony) | JPA (Spring Boot) |
|---|---|
$entityManager->persist($article) | articleRepository.save(article) |
$entityManager->flush() | (inclus dans save()) |
$articleRepository->find($id) | articleRepository.findById(id) |
$articleRepository->findAll() | articleRepository.findAll() |
Créer un repository avec JpaRepository
package org.joliciel.monblog.repository;
import org.joliciel.monblog.entity.Article;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ArticleRepository extends JpaRepository<Article, Long> {
// Spring génère automatiquement toutes les méthodes CRUD de base
}
JpaRepository<Article, Long> prend deux paramètres de type :
Article: le type de l'entité géréeLong: le type de la clé primaire (@Id)
Les méthodes disponibles sans rien écrire :
| Méthode | Description |
|---|---|
save(article) | Crée ou met à jour un article |
findById(id) | Cherche par id, retourne Optional<Article> |
findAll() | Retourne toutes les lignes sous forme de List<Article> |
delete(article) | Supprime un article |
deleteById(id) | Supprime par id |
count() | Nombre total de lignes |
existsById(id) | Vérifie si un id existe |
Injecter le repository dans un contrôleur
La manière recommandée est l'injection par constructeur (meilleure testabilité que @Autowired sur le champ) :
@Controller
public class ArticleController {
private final ArticleRepository articleRepository;
// Spring injecte automatiquement le repository via ce constructeur
public ArticleController(ArticleRepository articleRepository) {
this.articleRepository = articleRepository;
}
}
Avec Spring Boot, si le contrôleur n'a qu'un seul constructeur, l'annotation @Autowired sur ce constructeur est optionnelle. Spring la détecte automatiquement.
Contrôleur POST : recevoir un formulaire et sauvegarder
@PostMapping("/articles")
public String creerArticle(@ModelAttribute Article article,
RedirectAttributes redirectAttributes) {
articleRepository.save(article);
redirectAttributes.addFlashAttribute("message", "Article créé avec succès !");
return "redirect:/articles";
}
@PostMapping("/articles"): gère les requêtes HTTP POST sur/articles@ModelAttribute Article article: Spring remplit automatiquement l'objetArticleavec les champs du formulaire HTML (les noms des champsname="titre"doivent correspondre aux attributs Java)articleRepository.save(article): exécute unINSERTSQL si l'id estnull, unUPDATEsinonRedirectAttributes: permet de passer un message flash qui sera affiché après la redirectionreturn "redirect:/articles": renvoie le navigateur vers/articles(patron Post/Redirect/Get)
Ne retournez jamais directement une vue après un POST (return "articles/liste"). Utilisez toujours return "redirect:/...". Sans cela, si l'utilisateur actualise la page, le formulaire est soumis une deuxième fois, ce qui crée un doublon en base.
Messages flash avec RedirectAttributes
// Dans le contrôleur
redirectAttributes.addFlashAttribute("message", "Article créé !");
// Dans la vue Thymeleaf (articles/liste.html)
<div th:if="${message}" class="alert alert-success">
<span th:text="${message}"></span>
</div>
Les attributs flash sont stockés temporairement en session et supprimés automatiquement après un seul affichage.
Exemple pratique
Voici un contrôleur complet qui affiche le formulaire de création et sauvegarde l'article :
package org.joliciel.monblog.controller;
import org.joliciel.monblog.entity.Article;
import org.joliciel.monblog.repository.ArticleRepository;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
@Controller
@RequestMapping("/articles")
public class ArticleController {
private final ArticleRepository articleRepository;
public ArticleController(ArticleRepository articleRepository) {
this.articleRepository = articleRepository;
}
// Affiche le formulaire vide
@GetMapping("/nouveau")
public String afficherFormulaireCreation(Model model) {
model.addAttribute("article", new Article());
return "articles/nouveau";
}
// Reçoit le formulaire et sauvegarde
@PostMapping("/nouveau")
public String sauvegarderArticle(@ModelAttribute Article article,
RedirectAttributes redirectAttributes) {
articleRepository.save(article);
redirectAttributes.addFlashAttribute("message", "Article \"" + article.getTitre() + "\" créé avec succès !");
return "redirect:/articles";
}
// Liste tous les articles
@GetMapping
public String listerArticles(Model model) {
model.addAttribute("articles", articleRepository.findAll());
return "articles/liste";
}
}
Vue Thymeleaf templates/articles/nouveau.html :
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><title>Nouvel article</title></head>
<body>
<h1>Créer un article</h1>
<form th:action="@{/articles/nouveau}" th:object="${article}" method="post">
<div>
<label for="titre">Titre</label>
<input type="text" id="titre" th:field="*{titre}" />
</div>
<div>
<label for="contenu">Contenu</label>
<textarea id="contenu" th:field="*{contenu}"></textarea>
</div>
<button type="submit">Publier</button>
</form>
</body>
</html>
Test de mémorisation/compréhension
TP pour réfléchir et résoudre des problèmes
Dans ce TP, vous allez créer le formulaire de création d'un article dans le projet MonBlog.
Étape 1 — Créer le repository ArticleRepository
Créez une interface ArticleRepository dans le package repository.
Vous n'avez pas besoin d'écrire de code dans l'interface ArticleRepository. Spring génère automatiquement l'implémentation au démarrage de l'application. Toutes les méthodes CRUD (save, findAll, findById, delete...) sont déjà disponibles.
Étape 2 — Injecter le repository dans le contrôleur
Modifiez ArticleController pour injecter ArticleRepository par constructeur.
Préférez toujours l'injection par constructeur (public ArticleController(ArticleRepository repo)) à l'injection par champ (@Autowired private ArticleRepository repo). L'injection par constructeur rend les dépendances explicites, facilite les tests unitaires et permet de déclarer l'attribut final (immuable après construction).
Étape 3 — Créer la méthode POST pour sauvegarder
Après un POST réussi, redirigez toujours l'utilisateur avec redirect:/.... Ce patron (PRG - Post/Redirect/Get) empêche la double soumission du formulaire si l'utilisateur appuie sur F5 (actualiser). Sans redirection, F5 renverrait la même requête POST et créerait un doublon en base.