Spring Data MongoDB - Indici, annotazioni e convertitori

1. Panoramica

Questo tutorial esplorerà alcune delle funzionalità principali di Spring Data MongoDB: indicizzazione, annotazioni comuni e convertitori.

2. Indici

2.1. @Indexed

Questa annotazione contrassegna il campo come indicizzato in MongoDB:

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

Ora che il campo del nome è indicizzato, diamo un'occhiata agli indici in MongoDB:

db.user.getIndexes();

Ecco cosa abbiamo a livello di database:

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

Come puoi vedere, abbiamo due indici, uno di questi è _id , che è stato creato per impostazione predefinita a causa dell'annotazione @Id e il secondo è il nostro campo del nome .

2.2. Creare un indice a livello di codice

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 nome del campo e il risultato sarà lo stesso della sezione precedente.

2.3. Indici composti

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

Vediamo un rapido esempio di utilizzo degli indici composti:

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

Abbiamo creato un indice composto con i campi email e età . Vediamo ora gli indici effettivi:

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

Tieni presente che un campo DBRef non può essere contrassegnato con @Index : quel campo può essere solo parte di un indice composto.

3. Annotazioni comuni

3.1 @Transient

Come ci si aspetterebbe, questa semplice annotazione esclude la persistenza del campo 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 guardiamo lo stato del database, vediamo che il file yearOfBirth 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à nullo .

3.2. @Campo

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

@Field("email") private EmailAddress emailAddress; 

Ora emailAddress 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 @Value

@PersistenceConstructor contrassegna un costruttore, anche uno protetto da pacchetto, come il costruttore principale utilizzato dalla logica di persistenza. Gli argomenti del costruttore vengono mappati in base al nome ai valori chiave nel DBObject recuperato .

Diamo un'occhiata a questo costruttore per la nostra classe User :

@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 dell'annotazione standard Spring @Value qui. È con l'aiuto di questa annotazione che possiamo usare le Spring Expressions per trasformare il valore di una chiave recuperato dal database prima che venga utilizzato per costruire un oggetto di dominio. Questa è una caratteristica molto potente e molto utile qui.

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

Vediamo ora come funziona:

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

Our database will look:

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

So the age field is null, but when we query the document and retrieve age:

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

The result will be 0.

4. Converters

Let's now take a look at another very useful feature in Spring Data MongoDB – converters, and specifically at the MongoConverter.

This is used to handle the mapping of all Java types to DBObjects when storing and querying these objects.

We have two options – we can either work with MappingMongoConverter – or SimpleMongoConverter in earlier versions (this was deprecated in Spring Data MongoDB M3 and its functionality has been moved into MappingMongoConverter).

Or we can write our own custom converter. To do that, we would need to implement the Converter interface and register the implementation in MongoConfig.

Let's look at a quick example. As you've seen in some of the JSON output here, all objects saved in a database have the field _class which is saved automatically. If however we'd like to skip that particular field during persistence, we can do that using a MappingMongoConverter.

First – here's the custom converter implementation:

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

Notice how we can easily hit the goal of not persisting _class by specifically removing the field directly here.

Now we need to register the custom converter:

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

We can of course achieve the same result with XML configuration as well, if we need to:

Now, when we save a new user:

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

The resulting document in the database no longer contains the class information:

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

5. Conclusion

In this tutorial we've covered some core concepts of working with Spring Data MongoDB – indexing, common annotations and converters.

The implementation of all these examples and code snippets can be found in my github project.