Chapitre 3: Annotations Data et JPA
1. Annotations d'Entité
@Entity
Marque une classe comme entité JPA (table en base de données).
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private BigDecimal price;
}
@Table
Personnalise le nom de la table et ses contraintes.
@Entity
@Table(
name = "products",
schema = "ecommerce",
uniqueConstraints = @UniqueConstraint(columnNames = {"sku"}),
indexes = @Index(name = "idx_product_name", columnList = "name")
)
public class Product {
// ...
}
@Id
Marque le champ comme clé primaire.
@Id
private Long id;
@GeneratedValue
Stratégie de génération de la clé primaire.
// Auto-incrémentation (PostgreSQL SERIAL, MySQL AUTO_INCREMENT)
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// Séquence (recommandé pour PostgreSQL)
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "product_seq")
@SequenceGenerator(name = "product_seq", sequenceName = "product_sequence", allocationSize = 1)
private Long id;
// UUID
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private String id;
// La BDD choisit la stratégie
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
| Stratégie | Description | BDD typique |
|---|---|---|
IDENTITY | Auto-incrémentation | MySQL, PostgreSQL |
SEQUENCE | Séquence BDD | PostgreSQL, Oracle |
TABLE | Table de séquences | Toutes |
UUID | Identifiant UUID | Toutes |
AUTO | Choix automatique | Toutes |
@Column
Personnalise le mapping d'une colonne.
@Column(name = "product_name", nullable = false, length = 100)
private String name;
@Column(unique = true)
private String sku;
@Column(precision = 10, scale = 2)
private BigDecimal price;
@Column(columnDefinition = "TEXT")
private String description;
@Column(updatable = false) // Ne peut pas être modifié après la création
private LocalDateTime createdAt;
@Column(insertable = false) // Pas inclus dans les INSERT
private LocalDateTime updatedAt;
@Enumerated
Mappe un enum Java vers la base de données.
public enum Role {
USER, ADMIN, MANAGER
}
@Entity
public class User {
@Enumerated(EnumType.STRING) // Stocke "USER", "ADMIN" (RECOMMANDÉ)
private Role role;
@Enumerated(EnumType.ORDINAL) // Stocke 0, 1, 2 (DÉCONSEILLÉ)
private Role roleOrdinal;
}
@Temporal
Spécifie le type de date (pour java.util.Date uniquement, inutile avec java.time).
@Temporal(TemporalType.DATE) // Date seule
private Date birthDate;
@Temporal(TemporalType.TIMESTAMP) // Date + heure
private Date createdAt;
Avec Java 8+, utilisez LocalDate, LocalDateTime, Instant directement. @Temporal n'est pas nécessaire.
@Lob
Indique un champ Large Object (BLOB/CLOB).
@Lob
private String longDescription; // CLOB (texte long)
@Lob
private byte[] profileImage; // BLOB (binaire)
@Transient
Exclut un champ de la persistance (pas de colonne en BDD).
@Entity
public class User {
private String firstName;
private String lastName;
@Transient
private String fullName; // Calculé, pas stocké en BDD
public String getFullName() {
return firstName + " " + lastName;
}
}
2. Annotations de Relations
@OneToOne
Relation un-à-un.
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "address_id")
private Address address;
}
@Entity
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String street;
private String city;
@OneToOne(mappedBy = "address")
private User user;
}
@OneToMany / @ManyToOne
Relation un-à-plusieurs.
@Entity
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "department", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Employee> employees = new ArrayList<>();
}
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "department_id")
private Department department;
}
@ManyToMany
Relation plusieurs-à-plusieurs.
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany
@JoinTable(
name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private Set<Course> courses = new HashSet<>();
}
@Entity
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
@ManyToMany(mappedBy = "courses")
private Set<Student> students = new HashSet<>();
}
Résumé des paramètres de relation
| Paramètre | Description |
|---|---|
mappedBy | Côté inverse de la relation (pas de colonne FK) |
cascade | Propagation des opérations (PERSIST, MERGE, REMOVE, ALL) |
fetch | LAZY (chargement différé) ou EAGER (chargement immédiat) |
orphanRemoval | Supprimer les orphelins automatiquement |
optional | La relation peut être null |
3. Annotations de Requêtes
@Query
Définit une requête JPQL ou SQL native personnalisée.
public interface UserRepository extends JpaRepository<User, Long> {
// JPQL
@Query("SELECT u FROM User u WHERE u.email = :email")
Optional<User> findByEmail(@Param("email") String email);
// JPQL avec JOIN
@Query("SELECT u FROM User u JOIN u.orders o WHERE o.total > :amount")
List<User> findUsersWithOrdersAbove(@Param("amount") BigDecimal amount);
// Requête native SQL
@Query(value = "SELECT * FROM users WHERE created_at > :date", nativeQuery = true)
List<User> findRecentUsers(@Param("date") LocalDate date);
// Projection (retourne une partie des données)
@Query("SELECT new com.example.dto.UserSummary(u.id, u.name, u.email) FROM User u")
List<UserSummary> findAllSummaries();
}
@Param
Nomme un paramètre dans une requête @Query.
@Query("SELECT u FROM User u WHERE u.name = :name AND u.role = :role")
List<User> findByNameAndRole(@Param("name") String name, @Param("role") Role role);
@Modifying
Indique une requête de modification (UPDATE, DELETE).
@Modifying
@Transactional
@Query("UPDATE User u SET u.active = false WHERE u.lastLogin < :date")
int deactivateInactiveUsers(@Param("date") LocalDateTime date);
@Modifying
@Transactional
@Query("DELETE FROM User u WHERE u.active = false")
void deleteInactiveUsers();
@NamedQuery
Définit une requête nommée sur l'entité.
@Entity
@NamedQuery(name = "User.findActive", query = "SELECT u FROM User u WHERE u.active = true")
@NamedQuery(name = "User.countByRole", query = "SELECT COUNT(u) FROM User u WHERE u.role = :role")
public class User {
// ...
}
4. Annotations de Transaction et Repository
@Transactional
Gère les transactions de base de données.
@Service
@Transactional(readOnly = true) // Par défaut lecture seule
public class OrderService {
public List<Order> findAll() {
return orderRepository.findAll(); // Transaction en lecture seule
}
@Transactional // Transaction en écriture (override readOnly)
public Order createOrder(OrderDto dto) {
Order order = new Order();
// ... logique de création
return orderRepository.save(order);
}
@Transactional(
propagation = Propagation.REQUIRED, // Utilise la transaction existante ou en crée une
isolation = Isolation.READ_COMMITTED, // Niveau d'isolation
timeout = 30, // Timeout en secondes
rollbackFor = Exception.class // Rollback sur toute exception
)
public void processPayment(Long orderId) {
// Si une exception est lancée, tout est annulé
}
}
| Paramètre | Description |
|---|---|
readOnly | Optimisation pour les lectures |
propagation | Comportement de la transaction (REQUIRED, REQUIRES_NEW, etc.) |
isolation | Niveau d'isolation (READ_COMMITTED, SERIALIZABLE, etc.) |
timeout | Durée maximale en secondes |
rollbackFor | Exceptions qui déclenchent un rollback |
noRollbackFor | Exceptions qui ne déclenchent PAS de rollback |
@EnableJpaRepositories
Active les repositories Spring Data JPA (généralement auto-configuré par Spring Boot).
@Configuration
@EnableJpaRepositories(basePackages = "com.example.repository")
public class JpaConfig {
}
@EnableJpaAuditing
Active l'audit automatique (dates de création/modification).
@Configuration
@EnableJpaAuditing
public class JpaAuditConfig {
}
@Entity
@EntityListeners(AuditingEntityListener.class)
public class Article {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime updatedAt;
@CreatedBy
@Column(updatable = false)
private String createdBy;
@LastModifiedBy
private String updatedBy;
}
5. Annotations d'Héritage JPA
@Inheritance
Stratégie d'héritage entre entités.
// SINGLE_TABLE : toutes les sous-classes dans une seule table (par défaut)
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "type")
public abstract class Payment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private BigDecimal amount;
}
@Entity
@DiscriminatorValue("CARD")
public class CardPayment extends Payment {
private String cardNumber;
}
@Entity
@DiscriminatorValue("PAYPAL")
public class PaypalPayment extends Payment {
private String paypalEmail;
}
| Stratégie | Description | Performance |
|---|---|---|
SINGLE_TABLE | Une table pour toute la hiérarchie | Rapide (pas de JOIN) |
TABLE_PER_CLASS | Une table par sous-classe | Moyen |
JOINED | Table de base + tables par sous-classe avec JOIN | Normalisé mais plus lent |
6. Annotations Hibernate spécifiques
@Formula
Colonne calculée côté SQL (pas de colonne physique).
@Formula("(SELECT COUNT(*) FROM orders o WHERE o.user_id = id)")
private int orderCount;
@Where
Filtre SQL par défaut sur une collection.
@OneToMany(mappedBy = "user")
@Where(clause = "active = true")
private List<Order> activeOrders;
@BatchSize
Optimise le chargement des collections (prévient le problème N+1).
@OneToMany(mappedBy = "department")
@BatchSize(size = 20)
private List<Employee> employees;
Résumé Rapide
| Annotation | Rôle |
|---|---|
@Entity | Classe = table BDD |
@Table | Configuration de la table |
@Id | Clé primaire |
@GeneratedValue | Génération auto de l'ID |
@Column | Configuration d'une colonne |
@Enumerated | Mapping d'un enum |
@OneToOne | Relation 1:1 |
@OneToMany | Relation 1:N |
@ManyToOne | Relation N:1 |
@ManyToMany | Relation N:N |
@JoinColumn | Colonne de jointure (FK) |
@JoinTable | Table de jointure |
@Query | Requête personnalisée |
@Modifying | Requête UPDATE/DELETE |
@Transactional | Gestion des transactions |
@Transient | Champ non persisté |
➡️ Chapitre 4: Annotations Security, Test et Avancées