Spring Data MongoDB - Indizes, Anmerkungen und Konverter

1. Übersicht

In diesem Tutorial werden einige der Hauptfunktionen von Spring Data MongoDB erläutert - Indizierung, allgemeine Anmerkungen und Konverter.

2. Indizes

2.1. @Indexed

Diese Anmerkung markiert das Feld als in MongoDB indiziert :

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

Nun , da der Name Feld indiziert ist - lassen Sie uns einen Blick auf die Indizes in MongoDB haben:

db.user.getIndexes();

Folgendes haben wir auf Datenbankebene:

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

Wie Sie sehen können, haben wir zwei Indizes - einer von ihnen ist _id - die standardmäßig auf den Grund erstellt wurde @Id Annotation und die zweite ist unser Name Feld.

2.2. Programmgesteuert einen Index erstellen

Wir können auch programmgesteuert einen Index erstellen:

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

Wir haben nun einen Index für das Feld erstellt Namen und das Ergebnis wird das gleiche sein wie im vorherigen Abschnitt.

2.3. Zusammengesetzte Indizes

MongoDB unterstützt zusammengesetzte Indizes, bei denen eine einzelne Indexstruktur Verweise auf mehrere Felder enthält.

Sehen wir uns ein kurzes Beispiel mit zusammengesetzten Indizes an:

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

Wir haben einen zusammengesetzten Index mit den Feldern E-Mail und Alter erstellt . Schauen wir uns nun die tatsächlichen Indizes an:

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

Beachten Sie, dass ein DBRef- Feld nicht mit @Index markiert werden kann - dieses Feld kann nur Teil eines zusammengesetzten Index sein.

3. Allgemeine Anmerkungen

3.1 @Transient

Wie zu erwarten, schließt diese einfache Anmerkung aus, dass das Feld in der Datenbank beibehalten wird:

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

Fügen wir den Benutzer mit dem Einstellungsfeld yearOfBirth ein :

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

Wenn wir uns nun den Status der Datenbank ansehen, sehen wir, dass das abgelegte Geburtsjahr nicht gespeichert wurde:

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

Wenn wir also abfragen und prüfen:

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

Das Ergebnis ist null .

3.2. @Feld

@Field gibt den Schlüssel an, der für das Feld im JSON-Dokument verwendet werden soll:

@Field("email") private EmailAddress emailAddress; 

Jetzt wird emailAddress mit der Schlüssel- E-Mail in der Datenbank gespeichert :

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

Und der Zustand der Datenbank:

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

3.3. @PersistenceConstructor und @Value

@PersistenceConstructor marks a constructor, even one that's package protected, to be the primary constructor used by the persistence logic. The constructor arguments are mapped by name to the key values in the retrieved DBObject.

Let's look at this constructor for our User class:

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

Notice the use of the standard Spring @Value annotation here. It's with the help of this annotation that we can use the Spring Expressions to transform a key's value retrieved from the database before it is used to construct a domain object. That is a very powerful and highly useful feature here.

In our example if age is not set that it will be set to 0 by default.

Let's now see how it works:

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.