Spring Data MongoDB - Index, Annotations et Convertisseurs

Spring Data MongoDB - Index, Annotations et Convertisseurs

1. Vue d'ensemble

Ce didacticiel explore certaines des fonctionnalités essentielles de Spring Data MongoDB - indexation, annotations courantes et convertisseurs.

2. Les index

2.1. @Indexed

Cette annotationmarks the field as indexed dans MongoDB:

@QueryEntity
@Document
public class User {
    @Indexed
    private String name;

    ...
}

Maintenant que le champname est indexé, jetons un œil aux index dans MongoDB:

db.user.getIndexes();

Voici ce que nous avons au niveau de la base de données:

[
    {
        "v" : 1,
        "key" : {
             "_id" : 1
         },
        "name" : "_id_",
        "ns" : "test.user"
    },
    {
         "v" : 1,
         "key" : {
             "name" : 1
          },
          "name" : "name",
          "ns" : "test.user"
     }
]

Comme vous pouvez le voir, nous avons deux index - l'un d'eux est_id - qui a été créé par défaut en raison de l'annotation@Id etthe second one is our name field.

2.2. Créer un index par programmation

Nous pouvons également créer un index par programme:

mongoOps.indexOps(User.class).
  ensureIndex(new Index().on("name", Direction.ASC));

Nous avons maintenant créé un index pour le champname et le résultat sera le même que dans la section précédente.

2.3. Index composés

MongoDB prend en charge les index composés, dans lesquels une structure d'index unique contient des références à plusieurs champs.

Voyons un exemple rapide utilisant des index composés:

@QueryEntity
@Document
@CompoundIndexes({
    @CompoundIndex(name = "email_age", def = "{'email.id' : 1, 'age': 1}")
})
public class User {
    //
}

Nous avons créé un index composé avec les champsemail etage. Voyons maintenant les index réels:

{
    "v" : 1,
    "key" : {
        "email.id" : 1,
        "age" : 1
    },
    "name" : "email_age",
    "ns" : "test.user"
}

Notez qu'un champDBRef ne peut pas être marqué avec@Index - ce champ ne peut faire partie que d'un index composé.

3. Annotations courantes

3.1 @Transient

Comme vous vous en doutez, cette annotation simple empêche le champ d'être conservé dans la base de données:

public class User {

    @Transient
    private Integer yearOfBirth;
    // standard getter and setter

}

Insérons l'utilisateur avec le champ de réglageyearOfBirth:

User user = new User();
user.setName("Alex");
user.setYearOfBirth(1985);
mongoTemplate.insert(user);

Maintenant, si nous regardons l'état de la base de données, nous voyons que le fichieryearOfBirth n'a pas été sauvegardé:

{
    "_id" : ObjectId("55d8b30f758fd3c9f374499b"),
    "name" : "Alex",
    "age" : null
}

Donc, si nous interrogeons et vérifions:

mongoTemplate.findOne(Query.query(Criteria.where("name").is("Alex")), User.class).getYearOfBirth()

Le résultat seranull.

3.2. @Field

@Field indique la clé à utiliser pour le champ dans le document JSON:

@Field("email")
private EmailAddress emailAddress;

MaintenantemailAddress sera sauvegardé dans la base de données en utilisant la cléemail:

User user = new User();
user.setName("Brendan");
EmailAddress emailAddress = new EmailAddress();
emailAddress.setValue("[email protected]");
user.setEmailAddress(emailAddress);
mongoTemplate.insert(user);

Et l'état de la base de données:

{
    "_id" : ObjectId("55d076d80bad441ed114419d"),
    "name" : "Brendan",
    "age" : null,
    "email" : {
        "value" : "[email protected]"
    }
}

3.3. @PersistenceConstructor et@Value

@PersistenceConstructor marque un constructeur, même celui dont le package est protégé, comme étant le constructeur principal utilisé par la logique de persistance. Les arguments du constructeur sont mappés par nom aux valeurs de clé dans lesDBObject récupérés.

Regardons ce constructeur pour notre classeUser:

@PersistenceConstructor
public User(String name, @Value("#root.age ?: 0") Integer age, EmailAddress emailAddress) {
    this.name =  name;
    this.age = age;
    this.emailAddress =  emailAddress;
}

Remarquez l'utilisation de l'annotation standard Spring@Value ici. C’est à l’aide de cette annotation que nous pouvons utiliser les expressions Spring pour transformer la valeur d’une clé extraite de la base de données avant qu’elle ne soit utilisée pour construire un objet de domaine. C'est une fonctionnalité très puissante et très utile ici.

Dans notre exemple, siage n'est pas défini, il sera défini sur0 par défaut.

Voyons maintenant comment cela fonctionne:

User user = new User();
user.setName("Alex");
mongoTemplate.insert(user);

Notre base de données va chercher:

{
    "_id" : ObjectId("55d074ca0bad45f744a71318"),
    "name" : "Alex",
    "age" : null
}

Donc le champage estnull, mais lorsque nous interrogeons le document et récupéronsage:

mongoTemplate.findOne(Query.query(Criteria.where("name").is("Alex")), User.class).getAge();

Le résultat sera 0.

4. Convertisseurs

Jetons maintenant un coup d'œil à une autre fonctionnalité très utile de Spring Data MongoDB: les convertisseurs, et plus particulièrement lesMongoConverter.

Ceci est utilisé pour gérer le mappage de tous les types Java versDBObjects lors du stockage et de l'interrogation de ces objets.

Nous avons deux options - nous pouvons soit travailler avecMappingMongoConverter – ouSimpleMongoConverter dans les versions antérieures (cela était obsolète dans Spring Data MongoDB M3 et sa fonctionnalité a été déplacée dansMappingMongoConverter).

Ou nous pouvons écrire notre propre convertisseur personnalisé. Pour ce faire, nous aurions besoin d'implémenter l'interfaceConverter et d'enregistrer l'implémentation dansMongoConfig.

Regardonsa quick example. Comme vous l'avez vu dans une partie de la sortie JSON ici, tous les objets enregistrés dans une base de données ont le champ_class qui est enregistré automatiquement. Si toutefois nous souhaitons ignorer ce champ particulier pendant la persistance, nous pouvons le faire en utilisant unMappingMongoConverter.

Tout d'abord, voici l'implémentation du convertisseur personnalisé:

@Component
public class UserWriterConverter implements Converter {
    @Override
    public DBObject convert(User user) {
        DBObject dbObject = new BasicDBObject();
        dbObject.put("name", user.getName());
        dbObject.put("age", user.getAge());
        if (user.getEmailAddress() != null) {
            DBObject emailDbObject = new BasicDBObject();
            emailDbObject.put("value", user.getEmailAddress().getValue());
            dbObject.put("email", emailDbObject);
        }
        dbObject.removeField("_class");
        return dbObject;
    }
}

Remarquez comment nous pouvons facilement atteindre l'objectif de ne pas persister de_class en supprimant spécifiquement le champ directement ici.

Maintenant, nous devons enregistrer le convertisseur personnalisé:

private List> converters = new ArrayList>();

@Override
public MongoCustomConversions customConversions() {
    converters.add(new UserWriterConverter());
    return new MongoCustomConversions(converters);
}

Nous pouvons bien sûr obtenir le même résultat avec la configuration XML si nous devons:


    
    
    



    

Maintenant, quand on sauve un nouvel utilisateur:

User user = new User();
user.setName("Chris");
mongoOps.insert(user);

Le document résultant dans la base de données ne contient plus les informations de classe:

{
    "_id" : ObjectId("55cf09790bad4394db84b853"),
    "name" : "Chris",
    "age" : null
}

5. Conclusion

Dans ce didacticiel, nous avons couvert certains concepts de base de l'utilisation de Spring Data MongoDB: indexation, annotations courantes et convertisseurs.

L'implémentation de tous ces exemples et extraits de codecan be found inmy github project - il s'agit d'un projet basé sur Eclipse, il devrait donc être facile à importer et à exécuter tel quel.