MongoDB è un database orientato ai documenti con struttura JSON-like. I documenti sono caratterizzati da uno schema dinamico realizzato attraverso il formato BSON. Per schema dinamico intendiamo la possibilità di creare record senza una struttura predefinita, questo significa che possiamo avere record che condividono un certo numero di campi cosi come record che differiscono per il fatto di avere campi in più o in meno.
Il seguente confronto con le strutture fondamentali di un database relazionale può aiutare a comprendere meglio quanto appena detto:
- una collezione in MongoDB può essere vista come una tabella nel mondo relazionale;
- un documento in una collezione MongoDB, può essere visto come un record di una tabella nel mondo relazionale;
- un campo di un documento MongoDB può essere visto come una colonna di un record nel mondo relazionale;
- operazioni di JOIN vengono realizzate attraverso documenti collegati.
Per poter utilizzare MongoDB da applicazioni Java abbiamo la necessità di recuperare il relativo driver. In alternativa è possibile creare un progetto Maven inserendo la dipendenza:
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.4.2</version>
</dependency>
Ipotizziamo di avere in esecuzione MongoDB e di aver creato un database di nome testdb
. Iniziamo con il vedere quali siano le istruzioni per poter stabilire una connessione e visualizzare tutte le collezioni presenti sul database:
import com.mongodb.MongoClient;
import com.mongodb.client.MongoDatabase;
import java.util.logging.Level;
import java.util.logging.Logger;
public class MongoClientDemo {
public static void main(String[] args) {
Logger mongoLogger = Logger.getLogger( "org.mongodb.driver" );
mongoLogger.setLevel(Level.SEVERE);
MongoClient mongoClient = new MongoClient("localhost", 27017);
MongoDatabase database = mongoClient.getDatabase("testdb");
for (String name : database.listCollectionNames()) {
System.out.println(name);
}
mongoClient.close();
}
}
L'esecuzione di questa prima demo potrebbe non stampare nulla a causa della possibilità che il database di test non abbia ancora collezioni definite. Modifichiamo il codice
precedente in modo tale da creare una collezione Cliente
se non presente sul db:
boolean exists = false;
for (String name : database.listCollectionNames()) {
System.out.println(name);
if(name.equals("Cliente")) exists=true;
}
if(!exists) database.createCollection("Cliente");
mongoClient.close();
La collezione Cliente
è priva di documenti. Creiamo un documento nella collezione se la collezione stessa è inizialmente vuota (metodo count()
):
if(!exists) database.createCollection("Cliente");
MongoCollection<Document> clienteCollection = database.getCollection("Cliente");
if(clienteCollection.count()==0) {
Document document = new Document();
document.put("Nome", "Luigi");
document.put("Cognome", "Neri");
document.put("Lavoro", "Libero Professionista");
clienteCollection.insertOne(document);
}
for(Document document : clienteCollection.find()){
System.out.println(document);
}
Eseguendo nuovamente il codice di esempio dovremmo ottenere un output che mostra la struttura JSON del documento inserito:
Cliente
Document{{_id=..., Nome=Luigi, Cognome=Neri, Lavoro=Libero Professionista}}
. Evidenziamo l'utilizzo del metodo find()
per il recupero dei documenti presenti nella collezione.
Le operazioni CRUD in ambiente MongoDB si traducono nella possibilità di inserire, aggiornare e cancellare documenti all'interno di una collezione. Nel precedente esempio abbiamo anticipato l'operazione d'inserimento, vediamo adesso come eseguire le operazioni di aggiornamento e cancellazione di un singolo documento.
Per poter recuperare un documento, abbiamo l'esigenza di costruire un filtro inserendo i campi per un'identificazione univoca del documento stesso. Supponiamo per semplicità che per l'identificazione di un documento Cliente
sia sufficiente specificare il nome ed il cognome. Dopo aver recuperato la collezione Cliente
costruiamo un filtro di ricerca:
//Recupero di un documento
HashMap<String,Object> filterMap = new HashMap();
filterMap.put("Nome", "Luigi");
filterMap.put("Cognome", "Neri");
//Costruzione documento filtro
Bson filter = new Document(filterMap);
FindIterable<Document> docIterator = clienteCollection.find().filter(filter);
La costruzione del filtro di un documento si realizza attraverso il formato BSON. Una volta definita la mappa dei parametri identificativa del filtro la andiamo ad utilizzare nel metodo filter()
dell'oggetto restituito dal metodo find()
invocato sulla collezione dalla quale vogliamo estrarre il documento.
Il risultato attuale è il recupero di un iteratore di documenti sul risultato dell'estrazione documentale effettuata attraverso il filtro specificato. Sappiamo che il risultato dell'estrazione è costituito da un solo documento, recuperiamo questo risultato attraverso il metodo iterator()
che consente l'accesso ad un cursore Mongo e la successiva invocazione del metodo next()
:
MongoCursor<Document> mongoCursor = docIterator.iterator();
/*Procediamo subito con il next per questo test,
sappiamo di trovare un solo documento*/
Document clienteDoc = mongoCursor.next();
Una volta ottenuto il documento, possiamo aggiornarlo utilizzando nuovamente il formato BSON:
//Costruzione documento filtro
Bson newValue = new Document("Nome", "Ottavio");
//Costruzione documento di aggiornamento
Bson updateDocument = new Document("$set", newValue);
//Aggiornamento
clienteCollection.updateOne(clienteDoc, updateDocument);
In questo contesto il documento di filtro rappresenta il campo che intendiamo aggiornare. Per poter aggiornare il documento esistente è necessario costruire un nuovo documento che rappresenti l'aggiornamento stesso.
In riferimento al codice appena scritto, l'operazione di aggiornamento è specificata dalla stringa $set
, il valore di aggiornamento è invece rappresentato dal documento
costruito come filtro di aggiornamento sul campo(newValue).
Per aggiornare il documento originale in cui compare Nome=Luigi
con il nuovo valore Nome=Ottavio
, non dobbiamo fare altro che passare al metodo updateOne()
della collezione Cliente
sia il documento originale che il documento che ne rappresenta l'aggiornamento.
Concludiamo con la cancellazione di un documento che segue gli stessi passi di un aggiornamento con l'eccezione del metodo finale che esegue una cancellazione:
/*Costruzione nuovo documento filtro*/
HashMap<String,Object> filterMap = new HashMap();
filterMap.put("Nome", "Aldo");
filterMap.put("Cognome", "Rossi");
Bson filter = new Document(filterMap);
/*Recupero documento da cancellare*/
FindIterable<Document> docIterator = clienteCollection.find().filter(filter);
MongoCursor<Document> mongoCursor = docIterator.iterator();
/*Cancellazione documento*/
if(mongoCursor.hasNext()) {
Document clienteDoc = mongoCursor.next();
clienteCollection.deleteOne(clienteDoc);
System.out.println("Documento cancellato");
}