MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

Spring Data MongoDB – Indici, annotazioni e convertitori

1. Panoramica

In questo tutorial esploreremo alcune delle funzionalità principali di Spring Data MongoDB:indicizzazione, annotazioni comuni e convertitori.

2. Indici

2.1. @Indicizzato

Questa annotazione contrassegna il campo come indicizzato in MongoDB:

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

Ora che il nome il campo è indicizzato – diamo un'occhiata agli indici nella shell di MongoDB:

db.user.getIndexes();

Ecco cosa otteniamo:

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

Potremmo essere sorpresi che non ci sia alcun segno del nome campo ovunque!

Questo perché, a partire da Spring Data MongoDB 3.0, la creazione automatica dell'indice è disattivata per impostazione predefinita .

Tuttavia, possiamo modificare questo comportamento sovrascrivendo esplicitamente autoIndexCreation() metodo nel nostro MongoConfig :

public class MongoConfig extends AbstractMongoClientConfiguration {

    // rest of the config goes here

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

Esaminiamo di nuovo gli indici nella shell di MongoDB:

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

Come possiamo vedere, questa volta abbiamo due indici:uno di questi è _id – che è stato creato per impostazione predefinita a causa di @Id annotazione e il secondo è il nostro nome campo.

In alternativa,se utilizziamo Spring Boot, potremmo impostare spring.data.mongodb.auto-index-creation proprietà su vero .

2.2. Crea un indice in modo programmatico

Possiamo anche creare un indice a livello di codice:

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

Ora abbiamo creato un indice per il campo nome e il risultato sarà lo stesso della sezione precedente.

2.3. Indici composti

MongoDB supporta indici composti, in cui una singola struttura di indice contiene riferimenti a più campi.

Vediamo un rapido esempio di utilizzo di indici composti:

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

Abbiamo creato un indice composto con l'email e età campi. Diamo ora un'occhiata agli indici effettivi:

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

Nota che un DBRef il campo non può essere contrassegnato con @Index – quel campo può essere solo parte di un indice composto.

3. Annotazioni comuni

3.1. @Transitorio

Come ci si aspetterebbe, questa semplice annotazione esclude il campo dalla persistenza nel database:

public class User {
    
    @Transient
    private Integer yearOfBirth;
    // standard getter and setter

}

Inseriamo l'utente con il campo di impostazione yearOfBirth :

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

Ora, se osserviamo lo stato del database, vediamo che il file yearOfBirth archiviato non è stato salvato:

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

Quindi, se interroghiamo e controlliamo:

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

Il risultato sarà null .

3.2. @Campo

@Campo indica la chiave da utilizzare per il campo nel documento JSON:

@Field("email")
private EmailAddress emailAddress;

Ora indirizzo email verrà salvato nel database utilizzando la chiave email:

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

E lo stato del database:

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

3.3. @PersistenceConstructor e @Valore

@PersistenceConstructor contrassegna un costruttore, anche se protetto da pacchetto, come costruttore principale utilizzato dalla logica di persistenza. Gli argomenti del costruttore sono mappati per nome ai valori chiave nel DBObject recuperato .

Diamo un'occhiata a questo costruttore per il nostro Utente classe:

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

Notare l'uso dello standard Spring @Value annotazione qui. È con l'aiuto di questa annotazione che possiamo utilizzare le espressioni Spring per trasformare il valore di una chiave recuperato dal database prima che venga utilizzato per costruire un oggetto di dominio. Questa è una funzionalità molto potente e molto utile qui.

Nel nostro esempio se età non è impostato, verrà impostato su 0 per impostazione predefinita.

Vediamo ora come funziona:

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

Il nostro database cercherà:

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

Quindi l'età il campo è null , ma quando interroghiamo il documento e recuperiamo età :

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

Il risultato sarà 0.

4. Convertitori

Diamo ora un'occhiata a un'altra funzionalità molto utile in Spring Data MongoDB:i convertitori, e in particolare a MongoConverter .

Viene utilizzato per gestire la mappatura di tutti i tipi Java su DBObjects durante la memorizzazione e la query di questi oggetti.

Abbiamo due opzioni:possiamo lavorare con MappingMongoConverter – o SimpleMongoConverter nelle versioni precedenti (questo era deprecato in Spring Data MongoDB M3 e la sua funzionalità è stata spostata in MappingMongoConverter ).

Oppure possiamo scrivere il nostro convertitore personalizzato. Per farlo, avremmo bisogno di implementare il Convertitore interfaccia e registra l'implementazione in MongoConfig.

Diamo un'occhiata a un rapido esempio . Come abbiamo visto in alcuni degli output JSON qui, tutti gli oggetti salvati in un database hanno il campo _class che viene salvato automaticamente. Se tuttavia desideriamo saltare quel particolare campo durante la persistenza, possiamo farlo usando un MappingMongoConverter .

Primo:ecco l'implementazione del convertitore personalizzato:

@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;
    }
}

Nota come possiamo facilmente raggiungere l'obiettivo di non persistere _class rimuovendo specificamente il campo direttamente qui.

Ora dobbiamo registrare il convertitore personalizzato:

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

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

Ovviamente possiamo ottenere lo stesso risultato anche con la configurazione XML, se necessario:

<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>

Ora, quando salviamo un nuovo utente:

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

Il documento risultante nel database non contiene più le informazioni sulla classe:

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