Contactez-nous

Mapping de documents (`@Document`, `@Id`)

Apprenez les bases du mapping objet-document dans Spring Data MongoDB avec les annotations @Document pour lier une classe à une collection et @Id pour le champ _id.

Du POJO au Document : le mapping objet-document

Contrairement aux bases de données relationnelles qui stockent les données dans des tables structurées avec des lignes et des colonnes, MongoDB stocke les données sous forme de documents BSON (Binary JSON). Ces documents sont flexibles, auto-descriptifs et peuvent avoir des structures imbriquées complexes, ressemblant beaucoup aux objets JSON ou aux objets dans les langages de programmation.

Lorsque nous travaillons avec Spring Data MongoDB, notre objectif est de mapper nos classes Java (POJOs - Plain Old Java Objects) à ces documents MongoDB de manière transparente. Tout comme JPA utilise des annotations (@Entity, @Table, @Column, etc.) pour le mapping objet-relationnel, Spring Data MongoDB fournit son propre ensemble d'annotations pour définir comment une classe Java et ses champs correspondent à une collection MongoDB et à ses champs de document.

Les deux annotations les plus fondamentales et indispensables pour commencer ce mapping sont @Document et @Id.

Identifier la collection cible : l'annotation `@Document`

L'annotation @org.springframework.data.mongodb.core.mapping.Document est utilisée au niveau de la classe pour indiquer que cette classe Java doit être mappée à un document dans une collection MongoDB. Elle est l'équivalent conceptuel de l'annotation @Entity en JPA.

Par défaut, si vous annotez simplement une classe avec @Document sans spécifier d'attributs, Spring Data MongoDB supposera que le nom de la collection dans MongoDB correspond au nom de la classe Java, mais en commençant par une lettre minuscule. Par exemple, une classe UserAccount serait mappée par défaut à une collection nommée userAccount.

package com.myapp.model;

import org.springframework.data.mongodb.core.mapping.Document;

@Document // Mappe à la collection "product" par défaut
public class Product {
    // ... champs, getters, setters ...
}

Il est souvent préférable de spécifier explicitement le nom de la collection pour éviter toute ambiguïté et pour découpler le nom de la classe Java du nom de la collection dans la base de données. Pour cela, on utilise l'attribut collection (ou son alias value) de l'annotation @Document.

package com.myapp.model;

import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "products_catalog") // Mappe explicitement à la collection "products_catalog"
public class Product {
    // ... champs, getters, setters ...
}

En plus de l'attribut `collection`, l'annotation `@Document` possède d'autres attributs pour des configurations plus avancées, comme `language` pour l'indexation textuelle full-text ou `collation` pour définir les règles de comparaison de chaînes spécifiques à la langue.

L'identifiant unique : l'annotation `@Id` et le champ `_id`

Chaque document dans une collection MongoDB doit avoir un champ unique nommé _id qui sert de clé primaire. Ce champ est utilisé pour identifier de manière unique chaque document au sein de la collection. MongoDB peut générer automatiquement une valeur unique pour ce champ (un ObjectId) si vous n'en fournissez pas une lors de l'insertion.

Pour mapper un champ de votre classe Java à ce champ _id de MongoDB, vous devez utiliser l'annotation @org.springframework.data.annotation.Id (notez qu'il s'agit de l'annotation de Spring Data, pas de jakarta.persistence.Id de JPA). Cette annotation est placée sur le champ de votre POJO qui doit contenir l'identifiant du document.

Le type de champ que vous annotez avec @Id peut varier. Les types les plus courants sont :

  • String : Utile si vous souhaitez utiliser des identifiants personnalisés (comme des UUIDs sous forme de chaîne, des slugs, ou des identifiants métier).
  • org.bson.types.ObjectId : Type spécifique à MongoDB qui représente l'identifiant BSON standard généré automatiquement par MongoDB. C'est un type souvent utilisé si vous laissez MongoDB gérer la génération des IDs.
  • java.math.BigInteger : Peut aussi être utilisé.

Exemple avec un ID de type String :

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "users")
public class User {
    @Id
    private String id; // Sera mappé au champ _id de MongoDB

    private String username;
    private String email;
    // ... Getters, Setters ...
}

Exemple avec un ID de type ObjectId :

import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "orders")
public class Order {
    @Id
    private ObjectId id; // Sera mappé au champ _id de MongoDB (type ObjectId)

    private java.util.Date orderDate;
    private String customerId;
    // ... Getters, Setters ...
}

Gestion de la génération de l'ID

Comment l'ID est-il généré ? Spring Data MongoDB adopte une approche pragmatique :

  • Si le champ annoté @Id dans votre objet Java est null lorsque vous appelez la méthode save() du repository pour une nouvelle entité, Spring Data laissera MongoDB générer automatiquement un ObjectId et le mapper au champ _id du document inséré. Si votre champ @Id est de type ObjectId, il sera également renseigné dans votre objet Java après l'opération save(). Si votre champ @Id est de type String ou BigInteger, Spring Data convertira l'ObjectId généré en chaîne hexadécimale ou en BigInteger et renseignera votre champ.
  • Si le champ annoté @Id a déjà une valeur non nulle lorsque vous appelez save(), Spring Data utilisera cette valeur comme _id pour le document. Si un document avec cet _id existe déjà, l'opération save() effectuera une mise à jour (remplacement complet du document). Si aucun document n'existe avec cet _id, un nouveau document sera inséré avec l'_id que vous avez fourni.

Cela signifie que vous pouvez soit laisser MongoDB générer les IDs (ce qui est souvent le cas lorsque vous utilisez ObjectId), soit gérer vous-même la génération d'IDs uniques (par exemple, en générant des UUIDs et en les stockant dans un champ @Id de type String).

Mapping des autres champs par convention

Une fois que vous avez marqué votre classe avec @Document et identifié le champ ID avec @Id, comment les autres champs de votre classe sont-ils mappés ?

Par convention, Spring Data MongoDB mappe chaque champ non transitoire (c'est-à-dire non marqué avec @Transient de Spring Data ou le mot-clé transient de Java) de votre classe Java à un champ portant le même nom dans le document MongoDB.

@Document(collection = "articles")
public class Article {
    @Id
    private String id;
    
    private String title; // Mappé au champ "title" dans MongoDB
    private String authorName; // Mappé au champ "authorName" dans MongoDB
    private java.util.Date publicationDate; // Mappé au champ "publicationDate"
    
    @org.springframework.data.annotation.Transient
    private int internalCounter; // Ne sera pas persisté dans MongoDB

    // Getters, Setters...
}

Il existe bien sûr des annotations supplémentaires (comme @Field) pour personnaliser le nom du champ dans MongoDB ou pour gérer des types spécifiques, mais pour les cas simples, le mapping par convention est souvent suffisant.

Conclusion : les fondations du mapping MongoDB

Les annotations @Document et @Id constituent la base indispensable pour commencer à mapper vos objets Java à des documents MongoDB avec Spring Data. @Document lie votre classe à une collection spécifique, tandis que @Id désigne le champ Java qui correspondra à l'identifiant unique _id du document MongoDB.

En comprenant le rôle de ces deux annotations et la manière dont la génération d'ID est gérée, vous pouvez commencer à définir vos entités de manière efficace et préparer le terrain pour utiliser les interfaces Repository de Spring Data MongoDB afin d'interagir avec votre base de données NoSQL orientée document.