MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Spring Data MongoDB – Index, annotations et convertisseurs

1. Présentation

Dans ce didacticiel, nous allons explorer certaines des fonctionnalités principales de Spring Data MongoDB - l'indexation, les annotations courantes et les convertisseurs.

2. Index

2.1. @Indexé

Cette annotation marque le champ comme indexé dans MongoDB :

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

Maintenant que le nom le champ est indexé - regardons les index dans le shell MongoDB :

db.user.getIndexes();

Voici ce que nous obtenons :

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

Nous pourrions être surpris qu'il n'y ait aucun signe du nom champ n'importe où !

En effet, à partir de Spring Data MongoDB 3.0, la création automatique d'index est désactivée par défaut .

Nous pouvons cependant modifier ce comportement en remplaçant explicitement autoIndexCreation() méthode dans notre MongoConfig :

public class MongoConfig extends AbstractMongoClientConfiguration {

    // rest of the config goes here

    @Override
    protected boolean autoIndexCreation() {
        return true;
    }
}

Vérifions à nouveau les index dans le shell MongoDB :

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

Comme nous pouvons le voir, cette fois, nous avons deux index - l'un d'eux est _id – qui a été créé par défaut à cause du @Id annotation et la seconde est notre nom champ.

Alternativement, si nous utilisons Spring Boot, nous pourrions définir le spring.data.mongodb.auto-index-creation propriété à true .

2.2. Créer un index par programmation

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

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

Nous avons maintenant créé un index pour le champ nom 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, où une seule structure d'index 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 l'email et âge des champs. Voyons maintenant les index réels :

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

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

3. Annotations communes

3.1. @Transient

Comme on pouvait s'y attendre, cette simple annotation exclut la persistance du champ 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églage yearOfBirth :

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 fichier yearOfBirth n'a pas été enregistré :

{
    "_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 sera null .

3.2. @Champ

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

@Field("email")
private EmailAddress emailAddress;

Maintenant emailAddress sera enregistré dans la base de données à l'aide de 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 @Valeur

@PersistenceConstructor marque un constructeur, même s'il est protégé par un package, 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 le DBObject récupéré .

Regardons ce constructeur pour notre User classe :

@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 du standard Spring @Value annotation 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 si âge n'est pas défini, il sera défini sur 0 par défaut.

Voyons maintenant comment cela fonctionne :

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

Notre base de données ressemblera :

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

Donc l'âge le champ est null , mais lorsque nous interrogeons le document et récupérons age :

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

Le résultat sera 0.

4. Convertisseurs

Examinons maintenant une autre fonctionnalité très utile de Spring Data MongoDB - les convertisseurs, et plus particulièrement le MongoConverter .

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

Nous avons deux options – nous pouvons soit travailler avec MappingMongoConverter – ou SimpleMongoConverter dans les versions antérieures (ceci était obsolète dans Spring Data MongoDB M3 et sa fonctionnalité a été déplacée dans MappingMongoConverter ).

Ou nous pouvons écrire notre propre convertisseur personnalisé. Pour ce faire, nous aurions besoin d'implémenter le convertisseur interface et enregistrez l'implémentation dans MongoConfig.

Regardons un exemple rapide . Comme nous l'avons vu dans certaines des sorties 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 un MappingMongoConverter .

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

@Component
public class UserWriterConverter implements Converter<User, DBObject> {
    @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 _class en supprimant spécifiquement le champ directement ici.

Nous devons maintenant enregistrer le convertisseur personnalisé :

private List<Converter<?,?>> converters = new ArrayList<Converter<?,?>>();

@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 également, si nous en avons besoin :

<bean id="mongoTemplate" 
  class="org.springframework.data.mongodb.core.MongoTemplate">
    <constructor-arg name="mongo" ref="mongo"/>
    <constructor-arg ref="mongoConverter" />
    <constructor-arg name="databaseName" value="test"/>
</bean>

<mongo:mapping-converter id="mongoConverter" base-package="org.baeldung.converter">
    <mongo:custom-converters base-package="com.baeldung.converter" />
</mongo:mapping-converter>

Maintenant, lorsque nous enregistrons 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
}