Chapitre 4: Lombok - Réduire le Code Boilerplate
Introduction
Question: Combien de lignes de code pour une simple classe Product?
Sans Lombok: ~100 lignes (getters, setters, constructeurs, toString, equals, hashCode)
Avec Lombok: ~10 lignes
Lombok = Productivité 🚀
Qu'est-ce que Lombok?
Définition
Lombok est une bibliothèque Java qui génère automatiquement du code répétitif (boilerplate) grâce à des annotations.
Boilerplate = Code répétitif et ennuyeux 😴
Exemples:
- Getters et setters
- Constructeurs
toString()equals()ethashCode()
Analogie
Sans Lombok:
Vous êtes un chef qui doit:
- Laver les légumes
- Couper les légumes
- Cuisiner le plat
- Nettoyer la cuisine
Avec Lombok:
Vous dites "Préparez-moi un plat" et un assistant fait 1, 2, et 4 automatiquement. Vous vous concentrez sur 3 (la cuisine).
Installation de Lombok
Étape 1: Ajouter la Dépendance Maven
Fichier: pom.xml
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
Note: <optional>true</optional> indique que Lombok n'est utilisé qu'à la compilation.
Étape 2: Installer le Plugin IDE
VS Code
- Ouvrez Extensions (
Ctrl+Shift+XouCmd+Shift+X) - Recherchez: "Lombok Annotations Support for VS Code"
- Installez l'extension de Gabriel Basilio Brito
- Redémarrez VS Code
IntelliJ IDEA
- Allez dans File → Settings → Plugins
- Recherchez: "Lombok"
- Installez le plugin
- Redémarrez IntelliJ
Eclipse
- Téléchargez
lombok.jardepuis projectlombok.org - Exécutez:
java -jar lombok.jar - Sélectionnez votre installation Eclipse
- Redémarrez Eclipse
Étape 3: Vérification
Créez une classe de test:
package com.example.demo.model;
import lombok.Data;
@Data
public class TestLombok {
private String name;
private Integer age;
}
Compilez:
mvn clean compile
Vérifiez dans la console:
[INFO] BUILD SUCCESS
✅ Lombok fonctionne!
Annotations Principales
1. @Data
Génère automatiquement:
- Tous les getters
- Tous les setters
toString()equals()ethashCode()- Constructeur pour les champs
final
Avant Lombok
Fichier: model/Product.java
package com.example.demo.model;
import jakarta.persistence.*;
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Double price;
// Constructeur vide
public Product() {}
// Constructeur avec paramètres
public Product(String name, Double price) {
this.name = name;
this.price = price;
}
// Getters
public Long getId() {
return id;
}
public String getName() {
return name;
}
public Double getPrice() {
return price;
}
// Setters
public void setId(Long id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setPrice(Double price) {
this.price = price;
}
// toString()
@Override
public String toString() {
return "Product{" +
"id=" + id +
", name='" + name + '\'' +
", price=" + price +
'}';
}
// equals() et hashCode() (50+ lignes de plus)
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Product product = (Product) o;
return Objects.equals(id, product.id) &&
Objects.equals(name, product.name) &&
Objects.equals(price, product.price);
}
@Override
public int hashCode() {
return Objects.hash(id, name, price);
}
}
Total: ~100 lignes
Après Lombok
Fichier: model/Product.java
package com.example.demo.model;
import jakarta.persistence.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
@Entity
@Table(name = "products")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Double price;
}
Total: ~15 lignes
Réduction: De 100 lignes à 15 lignes! (85% de réduction) 🎉
2. @Getter / @Setter
Génère les getters/setters individuellement.
Exemple:
package com.example.demo.model;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
@Entity
public class Product {
@Id
@GeneratedValue
private Long id;
@Getter @Setter
private String name;
@Getter @Setter
private Double price;
}
Équivalent à:
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Double getPrice() { return price; }
public void setPrice(Double price) { this.price = price; }
Utilisation au niveau de la classe:
@Getter
@Setter
@Entity
public class Product {
private Long id;
private String name;
private Double price;
}
Génère tous les getters et setters pour tous les champs.
3. @NoArgsConstructor / @AllArgsConstructor
@NoArgsConstructor
Génère un constructeur sans arguments.
@NoArgsConstructor
@Entity
public class Product {
private Long id;
private String name;
private Double price;
}
Équivalent à:
public Product() {}
@AllArgsConstructor
Génère un constructeur avec tous les arguments.
@AllArgsConstructor
@Entity
public class Product {
private Long id;
private String name;
private Double price;
}
Équivalent à:
public Product(Long id, String name, Double price) {
this.id = id;
this.name = name;
this.price = price;
}
Combinaison courante:
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class Product {
private Long id;
private String name;
private Double price;
}
Génère:
Product()- Constructeur videProduct(Long id, String name, Double price)- Constructeur complet
4. @RequiredArgsConstructor
Génère un constructeur avec les champs final ou @NonNull.
Exemple:
@RequiredArgsConstructor
@Entity
public class Product {
private Long id;
@NonNull
private String name;
@NonNull
private Double price;
}
Génère:
public Product(String name, Double price) {
this.name = name;
this.price = price;
}
Note: id est exclu car non final et non @NonNull.
5. @Builder
Pattern Builder pour création d'objets.
Exemple:
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class Product {
@Id
@GeneratedValue
private Long id;
private String name;
private Double price;
}
Utilisation:
Product product = Product.builder()
.name("Laptop")
.price(999.99)
.build();
Avantages:
- Code lisible
- Paramètres nommés
- Ordre flexible
6. @ToString
Génère automatiquement toString().
Exemple:
@ToString
@Entity
public class Product {
private Long id;
private String name;
private Double price;
}
Équivalent à:
@Override
public String toString() {
return "Product(id=" + id + ", name=" + name + ", price=" + price + ")";
}
Exclure des champs:
@ToString(exclude = {"price"})
@Entity
public class Product {
private Long id;
private String name;
private Double price;
}
Résultat:
Product(id=1, name=Laptop)
7. @EqualsAndHashCode
Génère equals() et hashCode().
Exemple:
@EqualsAndHashCode
@Entity
public class Product {
private Long id;
private String name;
private Double price;
}
Équivalent à:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Product product = (Product) o;
return Objects.equals(id, product.id) &&
Objects.equals(name, product.name) &&
Objects.equals(price, product.price);
}
@Override
public int hashCode() {
return Objects.hash(id, name, price);
}
Utiliser uniquement l'ID:
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Entity
public class Product {
@EqualsAndHashCode.Include
@Id
private Long id;
private String name;
private Double price;
}
✅ Bonne pratique pour les entités JPA!
Combinaisons Courantes
Pour les Entités JPA
Configuration recommandée:
@Entity
@Table(name = "products")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank
private String name;
@Min(0)
private Double price;
}
Génère:
- Getters et setters
- Constructeur vide (requis par JPA)
- Constructeur complet
toString(),equals(),hashCode()
Pour les DTOs
Configuration recommandée:
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ProductDTO {
private Long id;
private String name;
private Double price;
}
Utilisation:
ProductDTO dto = ProductDTO.builder()
.id(1L)
.name("Laptop")
.price(999.99)
.build();
Pour les Services (Injection de Dépendances)
Configuration recommandée:
@Service
@RequiredArgsConstructor
public class ProductService {
private final ProductRepository productRepository;
// Pas besoin de constructeur!
public List<Product> findAll() {
return productRepository.findAll();
}
}
Lombok génère automatiquement:
public ProductService(ProductRepository productRepository) {
this.productRepository = productRepository;
}
Avantages et Inconvénients
Avantages
✅ Moins de code boilerplate (85% de réduction)
✅ Code plus lisible (focus sur la logique métier)
✅ Maintenance facilitée (pas de getters/setters à mettre à jour)
✅ Productivité accrue (développement plus rapide)
✅ Moins d'erreurs (code généré automatiquement)
Inconvénients
❌ Dépendance externe (ajout d'une bibliothèque)
❌ Nécessite plugin IDE (pour la complétion de code)
❌ Peut masquer la complexité (code invisible)
❌ Courbe d'apprentissage (pour les débutants)
❌ Débogage plus complexe (code généré non visible)
Meilleures Pratiques
1. Éviter @Data pour les Entités avec Relations
Problème:
@Entity
@Data // ATTENTION!
public class Order {
@Id
private Long id;
@OneToMany
private List<OrderItem> items;
}
Risque: toString() peut causer des boucles infinies avec les relations bidirectionnelles.
Solution:
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(exclude = {"items"})
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class Order {
@EqualsAndHashCode.Include
@Id
private Long id;
@OneToMany
private List<OrderItem> items;
}
2. Utiliser @RequiredArgsConstructor pour les Services
Bon:
@Service
@RequiredArgsConstructor
public class ProductService {
private final ProductRepository productRepository;
}
Mauvais:
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
}
Pourquoi? L'injection par constructeur est préférable (testabilité, immutabilité).
3. Toujours Combiner avec @NoArgsConstructor pour JPA
JPA requiert un constructeur vide:
@Entity
@Data
@NoArgsConstructor // OBLIGATOIRE pour JPA
@AllArgsConstructor
public class Product {
@Id
private Long id;
private String name;
}
Résumé
Annotations Essentielles
| Annotation | Génère |
|---|---|
@Data | Getters, Setters, toString, equals, hashCode |
@Getter / @Setter | Getters ou Setters |
@NoArgsConstructor | Constructeur vide |
@AllArgsConstructor | Constructeur complet |
@RequiredArgsConstructor | Constructeur avec final/@NonNull |
@Builder | Pattern Builder |
@ToString | toString() |
@EqualsAndHashCode | equals() et hashCode() |
Configuration JPA Standard
@Entity
@Table(name = "products")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Double price;
}
✅ Code minimal, fonctionnalité maximale!
Prochaine Étape
Vous avez maintenant tous les outils théoriques du Module 4! Passez au TP4.1 pour migrer votre application vers PostgreSQL avec Lombok.
➡️ TP4.1: Migration PostgreSQL