Aller au contenu principal

Créer des entités avec JPA

Notions théoriques

Qu'est-ce qu'une entité JPA ?

Une entité JPA est une classe Java dont les instances sont persistées en base de données. Chaque instance correspond à une ligne dans une table.

Comparaison avec Doctrine
Doctrine (Symfony)JPA (Spring Boot)
#[ORM\Entity]@Entity
#[ORM\Table(name: 'articles')]@Table(name = "articles")
#[ORM\Id]@Id
#[ORM\GeneratedValue]@GeneratedValue(strategy = GenerationType.IDENTITY)
#[ORM\Column]@Column
#[ORM\Column(type: 'text')]@Column(columnDefinition = "TEXT")

Annotations JPA essentielles

import jakarta.persistence.*;

@Entity // Déclare une entité JPA
@Table(name = "articles") // Nom de la table (optionnel si identique au nom de classe en snake_case)
public class Article {

@Id // Clé primaire
@GeneratedValue(strategy = GenerationType.IDENTITY) // Auto-incrémentée (comme AUTO_INCREMENT)
private Long id;

@Column(nullable = false, length = 255) // Contraintes SQL
private String titre;

@Column(columnDefinition = "TEXT") // Type TEXT (au lieu de VARCHAR)
private String contenu;

// Getters et setters...
}
Annotations Jakarta EE, pas javax

Depuis Spring Boot 3.x et Jakarta EE 9+, les annotations utilisent le package jakarta.persistence.* et non javax.persistence.*. Si IntelliJ propose les deux, choisissez toujours jakarta.

L'entité Article du projet MonBlog

package org.joliciel.monblog.entity;

import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import org.hibernate.annotations.CreationTimestamp;

import java.time.LocalDateTime;

@Entity
@Table(name = "articles")
public class Article {

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

@NotBlank(message = "Le titre est obligatoire")
@Size(min = 3, max = 255, message = "Le titre doit faire entre 3 et 255 caractères")
@Column(nullable = false, length = 255)
private String titre;

@NotBlank(message = "Le contenu est obligatoire")
@Column(columnDefinition = "TEXT", nullable = false)
private String contenu;

@CreationTimestamp // Remplie automatiquement à la création
@Column(name = "date_creation", updatable = false)
private LocalDateTime dateCreation;

// Constructeur sans argument requis par JPA
public Article() {}

// Constructeur pratique
public Article(String titre, String contenu) {
this.titre = titre;
this.contenu = contenu;
}

// Getters et setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }

public String getTitre() { return titre; }
public void setTitre(String titre) { this.titre = titre; }

public String getContenu() { return contenu; }
public void setContenu(String contenu) { this.contenu = contenu; }

public LocalDateTime getDateCreation() { return dateCreation; }
public void setDateCreation(LocalDateTime dateCreation) { this.dateCreation = dateCreation; }
}

Conventions de nommage

Élément JavaConventionExemple
Classe entitéPascalCase (singulier)Article, Utilisateur
Packagetout en minusculesentity ou domain
AttributscamelCasedateCreation, nomUtilisateur
Colonne SQLsnake_casedate_creation, nom_utilisateur

Bean Validation sur les entités

Les annotations de validation (package jakarta.validation.*) s'appliquent directement sur les attributs de l'entité :

@NotBlank(message = "Ce champ est obligatoire")
@Size(min = 2, max = 100)
@Email(message = "Format d'email invalide")
@Min(value = 0, message = "Doit être positif")
@Max(value = 150)
@Pattern(regexp = "^[A-Za-z]+$", message = "Lettres uniquement")

Ces annotations sont vérifiées automatiquement quand vous utilisez @Valid dans le contrôleur (séance 520).

L'annotation @CreationTimestamp

@CreationTimestamp (de Hibernate) remplit automatiquement la date et l'heure à la première sauvegarde de l'entité. Une fois sauvegardée, la valeur ne change plus grâce à updatable = false sur @Column.

info

En Doctrine Symfony, l'équivalent est #[ORM\Column(type: 'datetime_immutable')] #[Gedmo\Timestampable(on: 'create')] ou la méthode dans un lifecycle callback.

Exemple pratique

Voici l'entité Utilisateur qui sera utilisée pour l'authentification :

package org.joliciel.monblog.entity;

import jakarta.persistence.*;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

@Entity
@Table(name = "utilisateurs")
public class Utilisateur {

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

@NotBlank
@Email(message = "Format d'email invalide")
@Column(unique = true, nullable = false, length = 180)
private String email;

@NotBlank
@Column(nullable = false)
private String password; // Sera haché avec BCrypt (séance 532)

@Column(length = 50)
private String role; // "ROLE_USER" ou "ROLE_ADMIN"

// Constructeur sans argument (requis par JPA)
public Utilisateur() {}

public Utilisateur(String email, String password, String role) {
this.email = email;
this.password = password;
this.role = role;
}

// Getters et setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }

public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }

public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }

public String getRole() { return role; }
public void setRole(String role) { this.role = role; }
}

Test de mémorisation/compréhension


Quelle annotation JPA déclare qu'une classe est une entité persistée en base ?


Quelle annotation marque l'attribut comme clé primaire ?


Quelle stratégie de génération d'id correspond à AUTO_INCREMENT de MySQL ?


Dans Spring Boot 3.x, quel package contient les annotations JPA ?


Quelle annotation Hibernate remplit automatiquement la date de création d'une entité ?


Quelle annotation de validation vérifie qu'un String n'est pas vide ou nul ?


TP pour réfléchir et résoudre des problèmes

Dans ce TP, vous allez créer l'entité Article du projet MonBlog.

Étape 1 — Créer le package entity et la classe Article

Dans IntelliJ, créez un package entity sous org.joliciel.monblog, puis créez la classe Article.


Bonne pratique - Tables au pluriel

Les noms de tables SQL sont traditionnellement au pluriel (articles, utilisateurs) car une table contient plusieurs lignes. Les classes Java sont au singulier (Article, Utilisateur) car une instance représente un seul objet.

Étape 2 — Ajouter la clé primaire


Bonne pratique - Utiliser Long (et non int) pour les ids

Utilisez Long (objet, peut être null) plutôt que long (primitif) pour les clés primaires. Un id à null indique qu'un objet n'est pas encore sauvegardé en base — c'est une convention importante dans JPA.

Étape 3 — Ajouter le titre et le contenu avec validation


Bonne pratique - Dupliquer les contraintes en Java ET en SQL

Mettez les contraintes à la fois sur les annotations JPA (@Column(nullable = false)) ET sur les annotations de validation (@NotBlank). JPA crée les contraintes SQL, Bean Validation les vérifie en Java avant d'envoyer la requête. Les deux couches se complètent.

📌 Une solution