Einführung in Spring Data MongoDB

1. Übersicht

Dieser Artikel bietet eine schnelle und praktische Einführung in Spring Data MongoDB .

Wir werden die Grundlagen sowohl mit MongoTemplate als auch mit MongoRepository anhand praktischer Beispiele erläutern, um jede Operation zu veranschaulichen.

2. MongoTemplate und MongoRepository

Das MongoTemplate folgt im Frühjahr dem Standardvorlagenmuster und bietet eine sofort einsatzbereite Basis-API für die zugrunde liegende Persistenz-Engine.

Das Repository folgt dem Spring Data-zentrierten Ansatz und bietet flexiblere und komplexere API-Vorgänge, die auf den bekannten Zugriffsmustern in allen Spring Data-Projekten basieren.

Für beide müssen wir zunächst die Abhängigkeit definieren - zum Beispiel in der Datei pom.xml mit Maven:

 org.springframework.data spring-data-mongodb 3.0.3.RELEASE 

Um zu überprüfen, ob eine neue Version der Bibliothek veröffentlicht wurde, verfolgen Sie die Veröffentlichungen hier.

3. Konfiguration für MongoTemplate

3.1. XML-Konfiguration

Beginnen wir mit der einfachen XML-Konfiguration für die Mongo-Vorlage:

Zuerst müssen wir die Factory Bean definieren, die für die Erstellung von Mongo-Instanzen verantwortlich ist.

Als nächstes müssen wir die Template-Bean tatsächlich definieren (und konfigurieren):

Und schließlich müssen wir einen Postprozessor definieren, um alle MongoExceptions zu übersetzen , die in mit @Repository annotierten Klassen ausgelöst werden:

3.2. Java-Konfiguration

Lassen Sie uns nun eine ähnliche Konfiguration mit Java-Konfiguration erstellen, indem wir die Basisklasse für die MongoDB-Konfiguration erweitern. AbstractMongoConfiguration :

@Configuration public class MongoConfig extends AbstractMongoClientConfiguration { @Override protected String getDatabaseName() { return "test"; } @Override public MongoClient mongoClient() { ConnectionString connectionString = new ConnectionString("mongodb://localhost:27017/test"); MongoClientSettings mongoClientSettings = MongoClientSettings.builder() .applyConnectionString(connectionString) .build(); return MongoClients.create(mongoClientSettings); } @Override public Collection getMappingBasePackages() { return Collections.singleton("com.baeldung"); } }

Hinweis: In der vorherigen Konfiguration mussten wir die MongoTemplate- Bean nicht definieren, da sie bereits in AbstractMongoClientConfiguration definiert ist .

Wir können unsere Konfiguration auch von Grund auf neu verwenden, ohne AbstractMongoClientConfiguration zu erweitern - wie folgt:

@Configuration public class SimpleMongoConfig { @Bean public MongoClient mongo() { ConnectionString connectionString = new ConnectionString("mongodb://localhost:27017/test"); MongoClientSettings mongoClientSettings = MongoClientSettings.builder() .applyConnectionString(connectionString) .build(); return MongoClients.create(mongoClientSettings); } @Bean public MongoTemplate mongoTemplate() throws Exception { return new MongoTemplate(mongo(), "test"); } }

4. Konfiguration für MongoRepository

4.1. XML-Konfiguration

Um benutzerdefinierte Repositorys zu verwenden (Erweiterung des MongoRepository ), müssen Sie die Konfiguration ab Abschnitt 3.1 fortsetzen und die Repositorys einrichten:

4.2. Java-Konfiguration

In ähnlicher Weise bauen wir auf der Konfiguration auf, die wir bereits in Abschnitt 3.2 erstellt haben, und fügen dem Mix eine neue Anmerkung hinzu:

@EnableMongoRepositories(basePackages = "com.baeldung.repository") 

4.3. Erstellen Sie das Repository

Nach der Konfiguration müssen wir nun ein Repository erstellen, das die vorhandene MongoRepository- Schnittstelle erweitert:

public interface UserRepository extends MongoRepository { // }

Jetzt können wir dieses UserRepository automatisch verkabeln und Operationen aus MongoRepository verwenden oder benutzerdefinierte Operationen hinzufügen.

5. Verwenden von MongoTemplate

5.1. Einfügen

Beginnen wir mit der Einfügeoperation. Beginnen wir auch mit einer leeren Datenbank:

{ }

Wenn wir nun einen neuen Benutzer einfügen:

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

Die Datenbank sieht folgendermaßen aus:

{ "_id" : ObjectId("55b4fda5830b550a8c2ca25a"), "_class" : "com.baeldung.model.User", "name" : "Jon" }

5.2. Speichern - Einfügen

Der Speichervorgang verfügt über eine Semantik zum Speichern oder Aktualisieren: Wenn eine ID vorhanden ist, führt sie eine Aktualisierung durch, wenn nicht, führt sie eine Einfügung durch.

Schauen wir uns die erste Semantik an - die Einfügung; Hier ist der Anfangszustand der Datenbank :

{ }

Wenn wir jetzt einen neuen Benutzer speichern :

User user = new User(); user.setName("Albert"); mongoTemplate.save(user, "user");

Die Entität wird in die Datenbank eingefügt:

{ "_id" : ObjectId("55b52bb7830b8c9b544b6ad5"), "_class" : "com.baeldung.model.User", "name" : "Albert" }

Als nächstes betrachten wir dieselbe Operation - Speichern - mit Update-Semantik.

5.3. Speichern - Aktualisieren

Schauen wir uns nun das Speichern mit Update-Semantik an, das auf einer vorhandenen Entität ausgeführt wird:

{ "_id" : ObjectId("55b52bb7830b8c9b544b6ad5"), "_class" : "com.baeldung.model.User", "name" : "Jack" }

Wenn wir nun den vorhandenen Benutzer speichern, werden wir ihn aktualisieren:

user = mongoTemplate.findOne( Query.query(Criteria.where("name").is("Jack")), User.class); user.setName("Jim"); mongoTemplate.save(user, "user");

Die Datenbank sieht folgendermaßen aus:

{ "_id" : ObjectId("55b52bb7830b8c9b544b6ad5"), "_class" : "com.baeldung.model.User", "name" : "Jim" }

Wie Sie sehen können, verwendet save in diesem Beispiel die Semantik der Aktualisierung , da wir ein Objekt mit der angegebenen _id verwenden .

5.4. UpdateFirst

updateFirst aktualisiert das allererste Dokument, das der Abfrage entspricht.

Beginnen wir mit dem Anfangszustand der Datenbank:

[ { "_id" : ObjectId("55b5ffa5511fee0e45ed614b"), "_class" : "com.baeldung.model.User", "name" : "Alex" }, { "_id" : ObjectId("55b5ffa5511fee0e45ed614c"), "_class" : "com.baeldung.model.User", "name" : "Alex" } ]

Wenn wir jetzt das updateFirst ausführen :

Query query = new Query(); query.addCriteria(Criteria.where("name").is("Alex")); Update update = new Update(); update.set("name", "James"); mongoTemplate.updateFirst(query, update, User.class);

Nur der erste Eintrag wird aktualisiert:

[ { "_id" : ObjectId("55b5ffa5511fee0e45ed614b"), "_class" : "com.baeldung.model.User", "name" : "James" }, { "_id" : ObjectId("55b5ffa5511fee0e45ed614c"), "_class" : "com.baeldung.model.User", "name" : "Alex" } ]

5.5. UpdateMulti

UpdateMulti aktualisiert alle Dokumente, die der angegebenen Abfrage entsprechen .

Erstens - hier ist der Status der Datenbank vor dem UpdateMulti :

[ { "_id" : ObjectId("55b5ffa5511fee0e45ed614b"), "_class" : "com.baeldung.model.User", "name" : "Eugen" }, { "_id" : ObjectId("55b5ffa5511fee0e45ed614c"), "_class" : "com.baeldung.model.User", "name" : "Eugen" } ] 

Lassen Sie uns nun die Operation updateMulti ausführen:

Query query = new Query(); query.addCriteria(Criteria.where("name").is("Eugen")); Update update = new Update(); update.set("name", "Victor"); mongoTemplate.updateMulti(query, update, User.class);

Beide vorhandenen Objekte werden in der Datenbank aktualisiert:

[ { "_id" : ObjectId("55b5ffa5511fee0e45ed614b"), "_class" : "com.baeldung.model.User", "name" : "Victor" }, { "_id" : ObjectId("55b5ffa5511fee0e45ed614c"), "_class" : "com.baeldung.model.User", "name" : "Victor" } ]

5.6. FindAndModify

This operation works like updateMulti, but it returns the object before it was modified.

First – the state of the database before calling findAndModify:

{ "_id" : ObjectId("55b5ffa5511fee0e45ed614b"), "_class" : "com.baeldung.model.User", "name" : "Markus" } 

Let's look at the actual operation code:

Query query = new Query(); query.addCriteria(Criteria.where("name").is("Markus")); Update update = new Update(); update.set("name", "Nick"); User user = mongoTemplate.findAndModify(query, update, User.class);

The returned user object has the same values as the initial state in the database.

However, the new state in the database is:

{ "_id" : ObjectId("55b5ffa5511fee0e45ed614b"), "_class" : "com.baeldung.model.User", "name" : "Nick" }

5.7. Upsert

The upsert works operate on the find and modify else create semantics: if the document is matched, update it, else create a new document by combining the query and update object.

Let's start with the initial state of the database:

{ "_id" : ObjectId("55b5ffa5511fee0e45ed614b"), "_class" : "com.baeldung.model.User", "name" : "Markus" }

Now – let's run the upsert:

Query query = new Query(); query.addCriteria(Criteria.where("name").is("Markus")); Update update = new Update(); update.set("name", "Nick"); mongoTemplate.upsert(query, update, User.class);

Here's the state of the database after the operation:

{ "_id" : ObjectId("55b5ffa5511fee0e45ed614b"), "_class" : "com.baeldung.model.User", "name" : "Nick" }

5.8. Remove

The state of the database before calling remove:

{ "_id" : ObjectId("55b5ffa5511fee0e45ed614b"), "_class" : "com.baeldung.model.User", "name" : "Benn" }

Let's now run remove:

mongoTemplate.remove(user, "user");

The result will be as expected:

{ }

6. Using MongoRepository

6.1. Insert

First – the state of the database before running the insert:

{ }

Now, when we insert a new user:

User user = new User(); user.setName("Jon"); userRepository.insert(user); 

Here's the end state of the database:

{ "_id" : ObjectId("55b4fda5830b550a8c2ca25a"), "_class" : "com.baeldung.model.User", "name" : "Jon" }

Note how the operation works the same as the insert in the MongoTemplate API.

6.2. Save Insert

Similarly – save works the same as the save operation in the MongoTemplate API.

Let's start by looking at the insert semantics of the operation; here's the initial state of the database:

{ }

Now – we execute the save operation:

User user = new User(); user.setName("Aaron"); userRepository.save(user);

This results in the user being added to the database:

{ "_id" : ObjectId("55b52bb7830b8c9b544b6ad5"), "_class" : "com.baeldung.model.User", "name" : "Aaron" }

Note again how, in this example, save works with insert semantics, because we are inserting a new object.

6.3. Save Update

Let's now look at the same operation but with update semantics.

First – here's the state of the database before running the new save:

{ "_id" : ObjectId("55b52bb7830b8c9b544b6ad5"), "_class" : "com.baeldung.model.User", "name" : "Jack"81*6 }

Now – we execute the operation:

user = mongoTemplate.findOne( Query.query(Criteria.where("name").is("Jack")), User.class); user.setName("Jim"); userRepository.save(user);

Finally, here is the state of the database:

{ "_id" : ObjectId("55b52bb7830b8c9b544b6ad5"), "_class" : "com.baeldung.model.User", "name" : "Jim" }

Note again how, in this example, save works with update semantics, because we are using an existing object.

6.4. Delete

The state of the database before calling delete:

{ "_id" : ObjectId("55b5ffa5511fee0e45ed614b"), "_class" : "com.baeldung.model.User", "name" : "Benn" }

Let's run delete:

userRepository.delete(user); 

The result will simply be:

{ }

6.5. FindOne

The state of the database when findOne is called:

{ "_id" : ObjectId("55b5ffa5511fee0e45ed614b"), "_class" : "com.baeldung.model.User", "name" : "Chris" }

Let's now execute the findOne:

userRepository.findOne(user.getId()) 

The result which will return the existing data:

{ "_id" : ObjectId("55b5ffa5511fee0e45ed614b"), "_class" : "com.baeldung.model.User", "name" : "Chris" }

6.6. Exists

The state of the database before calling exists:

{ "_id" : ObjectId("55b5ffa5511fee0e45ed614b"), "_class" : "com.baeldung.model.User", "name" : "Harris" }

Now, let's run exists:

boolean isExists = userRepository.exists(user.getId());

Which of course will return true.

6.7. FindAll W ith Sort

The state of the database before calling findAll:

[ { "_id" : ObjectId("55b5ffa5511fee0e45ed614b"), "_class" : "com.baeldung.model.User", "name" : "Brendan" }, { "_id" : ObjectId("67b5ffa5511fee0e45ed614b"), "_class" : "com.baeldung.model.User", "name" : "Adam" } ]

Let's now run findAll with Sort:

List users = userRepository.findAll(Sort.by(Sort.Direction.ASC, "name"));

The result will be sorted by name in ascending order:

[ { "_id" : ObjectId("67b5ffa5511fee0e45ed614b"), "_class" : "com.baeldung.model.User", "name" : "Adam" }, { "_id" : ObjectId("55b5ffa5511fee0e45ed614b"), "_class" : "com.baeldung.model.User", "name" : "Brendan" } ]

6.8. FindAll W ith Pageable

The state of the database before calling findAll:

[ { "_id" : ObjectId("55b5ffa5511fee0e45ed614b"), "_class" : "com.baeldung.model.User", "name" : "Brendan" }, { "_id" : ObjectId("67b5ffa5511fee0e45ed614b"), "_class" : "com.baeldung.model.User", "name" : "Adam" } ]

Let's now execute findAll with a pagination request:

Pageable pageableRequest = PageRequest.of(0, 1); Page page = userRepository.findAll(pageableRequest); List users = pages.getContent();

The resulting users list will be only one user:

{ "_id" : ObjectId("55b5ffa5511fee0e45ed614b"), "_class" : "com.baeldung.model.User", "name" : "Brendan" }

7. Annotations

Finally, let's also go over the simple annotations that Spring Data uses to drive these API operations.

@Id private String id;

The field level @Id annotation can decorate any type, including long and string.

If the value of the @Id field is not null, it's stored in the database as-is; otherwise, the converter will assume you want to store an ObjectId in the database (either ObjectId, String or BigInteger work).

Next – @Document:

@Document public class User { // }

This annotation simply marks a class as being a domain object that needs to be persisted to the database, along with allowing us to choose the name of the collection to be used.

8. Conclusion

This article was a quick but comprehensive introduction to using MongoDB with Spring Data, both via the MongoTemplate API as well as making use of MongoRepository.

The implementation of all these examples and code snippets can be found over on Github.