Exploiter les Records Java pour simplifier les DTO et les POJO immuables
Découvrez comment les records, introduits avec Java 14, permettent de réduire le code boilerplate des DTO et de créer des objets immuables facilement, tout en restant compatibles avec les frameworks courants.
Introduction
Les Data Transfer Objects (DTO) et les POJO immuables sont omniprésents dans les applications Java, notamment lors de la communication entre services ou de la persistance. Jusqu’à Java 13, leur implémentation nécessitait la rédaction de getters, de constructeurs, de equals, hashCode et de toString, ce qui alourdit le code et augmente le risque d’erreurs. Depuis Java 14, les records offrent une syntaxe concise pour déclarer des classes immutables dont le comportement est généré automatiquement par le compilateur.
Créer un record : syntaxe de base
Un record se déclare en spécifiant les composants du type dans la signature. Le compilateur génère automatiquement :
- Un constructeur canonique
- Les accesseurs (
componentName()) equals,hashCodeettoString
public record UserDto(Long id, String username, String email) {}
Ce petit morceau de code remplace plusieurs dizaines de lignes d’une classe POJO traditionnelle. Le record est final et ses champs sont implicitly private final, garantissant l’immuabilité.
Comparaison avec une classe POJO traditionnelle
| Aspect | POJO classique | Record |
|---|---|---|
| Déclaration | private fields + getters + setters + constructeur + equals/hashCode/toString | Une seule ligne de déclaration |
| Mutabilité | Variable selon l’implémentation (souvent mutable) | Immuable par défaut |
| Verbosité | Élevée (30‑40 lignes) | Très faible (1‑2 lignes) |
| Extensibilité | Possible via héritage | Aucun héritage (les records ne peuvent pas être étendus) |
Cette comparaison montre clairement le gain de lisibilité et de maintenance offert par les records.
Utilisation avec les frameworks populaires
Jackson
Jackson, la bibliothèque de sérialisation JSON la plus répandue, supporte les records depuis la version 2.12. Il suffit d’ajouter le module jackson-databind et, éventuellement, le module jackson-modules-java8.
ObjectMapper mapper = new ObjectMapper();
UserDto user = new UserDto(1L, "alice", "alice@example.com");
String json = mapper.writeValueAsString(user);
// {"id":1,"username":"alice","email":"alice@example.com"}
UserDto deserialized = mapper.readValue(json, UserDto.class);
Jackson utilise le constructeur canonique du record pour désérialiser les données, aucune annotation supplémentaire n’est requise.
JPA / Hibernate
Les records ne sont pas directement compatibles avec JPA, car les entités doivent disposer d’un constructeur sans argument et de setters. Cependant, on peut combiner un record avec une entité en gardant le record comme Vue DTO et en laissant l’entité mutable. Une alternative consiste à mapper le record vers l’entité via des frameworks de mapping (MapStruct, ModelMapper).
@Entity
public class UserEntity {
@Id @GeneratedValue private Long id;
private String username;
private String email;
// getters, setters, constructors …
}
@Mapper(componentModel = "spring")
public interface UserMapper {
UserDto toDto(UserEntity entity);
UserEntity toEntity(UserDto dto);
}
Cette approche conserve les avantages immutables du record pour les couches externes tout en respectant les exigences de JPA.
Bonnes pratiques et limitations
- Pas d’héritage : les records ne peuvent pas étendre d’autres classes (sauf
java.lang.Record). Concevez votre modèle en conséquence. - Validation : placez la logique de validation dans le constructeur canonique ou utilisez des annotations Bean Validation (
@NotNull,@Size). - Complexité des champs : les composants du record doivent être des types immutables ou bien être traités comme tels. Évitez les collections mutables directement; préférez
List<String>avecCollections.unmodifiableListou des typesjava.time. - Interopérabilité : assurez‑vous que les bibliothèques que vous utilisez supportent les records (Jackson, Gson, Spring Boot 2.5+, etc.). Sinon, recourez à des mixins ou à des wrappers classiques.
Conclusion
Les records Java offrent une solution élégante pour réduire le boilerplate des DTO et garantir l’immuabilité des objets. En les combinant avec les bibliothèques de sérialisation et de mapping modernes, vous obtenez un code plus lisible, plus sûr et plus facile à maintenir, tout en restant compatible avec les exigences des certifications Java qui valorisent les bonnes pratiques de conception.
Envie d’aller plus loin avec CertifApp ?
Découvrir CertifApp