Aller au contenu principal

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égieDescriptionBDD typique
IDENTITYAuto-incrémentationMySQL, PostgreSQL
SEQUENCESéquence BDDPostgreSQL, Oracle
TABLETable de séquencesToutes
UUIDIdentifiant UUIDToutes
AUTOChoix automatiqueToutes

@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;
astuce

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ètreDescription
mappedByCôté inverse de la relation (pas de colonne FK)
cascadePropagation des opérations (PERSIST, MERGE, REMOVE, ALL)
fetchLAZY (chargement différé) ou EAGER (chargement immédiat)
orphanRemovalSupprimer les orphelins automatiquement
optionalLa 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ètreDescription
readOnlyOptimisation pour les lectures
propagationComportement de la transaction (REQUIRED, REQUIRES_NEW, etc.)
isolationNiveau d'isolation (READ_COMMITTED, SERIALIZABLE, etc.)
timeoutDurée maximale en secondes
rollbackForExceptions qui déclenchent un rollback
noRollbackForExceptions 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égieDescriptionPerformance
SINGLE_TABLEUne table pour toute la hiérarchieRapide (pas de JOIN)
TABLE_PER_CLASSUne table par sous-classeMoyen
JOINEDTable de base + tables par sous-classe avec JOINNormalisé 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​

AnnotationRôle
@EntityClasse = table BDD
@TableConfiguration de la table
@IdClé primaire
@GeneratedValueGénération auto de l'ID
@ColumnConfiguration d'une colonne
@EnumeratedMapping d'un enum
@OneToOneRelation 1:1
@OneToManyRelation 1:N
@ManyToOneRelation N:1
@ManyToManyRelation N:N
@JoinColumnColonne de jointure (FK)
@JoinTableTable de jointure
@QueryRequête personnalisée
@ModifyingRequête UPDATE/DELETE
@TransactionalGestion des transactions
@TransientChamp non persisté

➡️ Chapitre 4: Annotations Security, Test et Avancées


⬅️ Retour au Module 17