Introduzione a Spring Data MongoDB

1. Panoramica

Questo articolo sarà un'introduzione rapida e pratica a Spring Data MongoDB .

Esamineremo le basi utilizzando sia MongoTemplate che MongoRepository utilizzando esempi pratici per illustrare ciascuna operazione.

2. MongoTemplate e MongoRepository

Il MongoTemplate segue il modello di modello standard in Spring e fornisce un'API di base pronta all'uso per il motore di persistenza sottostante.

Il repository segue l'approccio Spring Data-centric e viene fornito con operazioni API più flessibili e complesse, basate sui modelli di accesso ben noti in tutti i progetti Spring Data.

Per entrambi, dobbiamo iniziare definendo la dipendenza, ad esempio nel pom.xml , con Maven:

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

Per verificare se è stata rilasciata una nuova versione della libreria, tenere traccia delle versioni qui.

3. Configurazione per MongoTemplate

3.1. Configurazione XML

Cominciamo con la semplice configurazione XML per il modello Mongo:

Innanzitutto, dobbiamo definire il factory bean responsabile della creazione delle istanze Mongo.

Successivamente, dobbiamo definire (e configurare) il bean del modello:

Infine, dobbiamo definire un post processor per tradurre qualsiasi MongoExceptions generata nelle classi annotate @Repository :

3.2. Configurazione Java

Creiamo ora una configurazione simile usando Java config estendendo la classe base per la configurazione MongoDB 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"); } }

Nota: non era necessario definire il bean MongoTemplate nella configurazione precedente poiché è già definito in AbstractMongoClientConfiguration.

Possiamo anche usare la nostra configurazione da zero senza estendere AbstractMongoClientConfiguration , come segue:

@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. Configurazione per MongoRepository

4.1. Configurazione XML

Per utilizzare i repository personalizzati (estendendo MongoRepository ), è necessario continuare la configurazione dalla sezione 3.1 e impostare i repository:

4.2. Configurazione Java

Allo stesso modo, costruiremo sulla configurazione che abbiamo già creato nella sezione 3.2 e aggiungeremo una nuova annotazione nel mix:

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

4.3. Crea il repository

Ora, dopo la configurazione, dobbiamo creare un repository, estendendo l' interfaccia MongoRepository esistente :

public interface UserRepository extends MongoRepository { // }

Ora possiamo collegare automaticamente questo UserRepository e utilizzare operazioni da MongoRepository o aggiungere operazioni personalizzate.

5. Utilizzo di MongoTemplate

5.1. Inserire

Cominciamo con l'operazione di inserimento; iniziamo anche con un database vuoto:

{ }

Ora se inseriamo un nuovo utente:

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

Il database sarà simile a questo:

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

5.2. Salva - Inserisci

L' operazione di salvataggio ha una semantica di salvataggio o aggiornamento: se è presente un id, esegue un aggiornamento, altrimenti esegue un inserimento.

Diamo un'occhiata alla prima semantica: l'inserto; ecco lo stato iniziale del database :

{ }

Quando ora salviamo un nuovo utente:

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

L'entità verrà inserita nel database:

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

Successivamente, esamineremo la stessa operazione - salva - con la semantica di aggiornamento.

5.3. Salva - Aggiorna

Diamo ora un'occhiata al salvataggio con la semantica di aggiornamento, operando su un'entità esistente:

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

Ora, quando salviamo l'utente esistente, lo aggiorneremo:

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

Il database sarà simile a questo:

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

As you can see, in this particular example, save uses the semantics of update, because we use an object with given _id.

5.4. UpdateFirst

updateFirst updates the very first document that matches the query.

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

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

When we now run the updateFirst:

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

Only the first entry will be updated:

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

5.5. UpdateMulti

UpdateMultiupdates all document that matches the given query.

First – here's the state of the database before doing the updateMulti:

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

Now, let's now run the updateMulti operation:

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

Both existing objects will be updated in the database:

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