Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

API per Java

Le principali funzionalità di mongo-java-driver, il driver ufficiale per l'interazione con un database MongoDB tramite Java.
Le principali funzionalità di mongo-java-driver, il driver ufficiale per l'interazione con un database MongoDB tramite Java.
Link copiato negli appunti

In questa prima appendice rivedremo in azione alcune funzioni di MongoDB con l’API Java, allo scopo di familiarizzare con questo driver. Molti di questi concetti sono applicabili anche agli altri driver.

Setup del progetto

Come abbiamo già visto nella quarta lezione, per utilizzare mongo-java-driver, il driver ufficiale, si può utilizzare Maven. Utilizzeremo la versione 2 del driver, dal momento che la versione 3 è ancora in beta e non consigliata in produzione. Sulla versione 3, possiamo dire che le principali novità riguardano l’introduzione di una API alternativa asincrona, di cui parleremo brevemente al termine di questa appendice. Per quanto riguarda l’API sincrona, le due versioni sono abbastanza simili, se non per qualche nome di classe modificato, che comunque rimarcheremo.

  1. Per prima cosa apriamo il nostro IDE preferito. Noi utilizzeremo NetBeans 8. Per gli altri ovviamente rimandiamo alle rispettive guide, ma in generale le procedure sono relativamente simili.
  2. Creiamo un nuovo progetto e selezioniamo, dalla categoria Maven, una Java application:

    Figura 1. Creazione di un nuovo progetto Maven (click per ingrandire)

    Creazione di un nuovo  progetto Maven
  3. Diamo un nome al nostro progetto, e creiamolo cliccando su Finish.
  4. Esplodiamo il nodo Project Files nel tab Projects in NetBeans e apriamo il file pom.xml, dove inseriremo la dipendenza mongo-java-driver. Il file pom.xml diventerà simile al seguente:
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.mycompany</groupId>
        <artifactId>lezione19</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>jar</packaging>
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.compiler.source>1.7</maven.compiler.source>
            <maven.compiler.target>1.7</maven.compiler.target>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.mongodb</groupId>
                <artifactId>mongo-java-driver</artifactId>
                <version>2.12.0</version>
            </dependency>
        </dependencies>
    </project>
  5. Ora compiliamo, in modo da lasciare che Maven effettui il download delle dipendenze. Se tutto è andato a buon fine, il nostro progetto risulterà compilato e saremo pronti per iniziare.

Gestione della connessione

Per connettersi a MongoDB si usa la classe MongoClient. Questa classe gestisce internamente un pool di connessioni. Questo significa che:

  1. Non dobbiamo occuparci noi di quante connessioni sono aperte, né di monitorarle per riusarle e così via.
  2. La classe è thread safe, quindi possiamo (ed anzi dovremmo) istanziare un oggetto MongoClient allo startup ed utilizzarlo per tutta la vita dell’applicazione, condividendolo con tutti i thread dell’applicazione. Quindi, in caso di applicazione web, questa istanza dovrebbe essere condivisa con tutte le request e le sessioni degli utenti.

La sintassi per creare un MongoClient è:

try {
    MongoClient mongoClient = new MongoClient( "localhost", 27017 );
} catch (UnknownHostException ex) {
    Logger.getLogger(ConnectionProvider.class.getName()).log(Level.SEVERE, null, ex);
}

Per noi, MongoClient è trasparente riguardo al fatto che il server sia composto da un nodo singolo o da un cluster di shard. Se ci connettiamo ad un Replica Set, tuttavia, la sintassi raccomandata è la seguente:

List<ServerAddress> servers = new ArrayList<>();
    servers.add(new ServerAddress("server1", 27017));
    servers.add(new ServerAddress("server2", 27017));
    servers.add(new ServerAddress("server3", 27017));
    MongoClient mongoClient = new MongoClient(servers);

MongoClient cercherà tra questi indirizzi il server primario correntemente eletto.

Una volta creato, questo oggetto può essere usato in tutti i thread dell’applicazione, sia per iniezione tramite un container tipo Spring o Guice, sia più banalmente in un getter statico. Tranne il caso in cui la nostra applicazione abbia bisogno di accedere a diverse istanze MongoDB, non avremo mai bisogno di creare altri MongoClient per accedere al database, e anzi è meglio non farlo per non sprecare risorse sul client.

Mappatura dei tipi

I tipi BSON si mappano su Java in modo molto semplice. Vediamo subito un esempio, estendendo quello visto nella lezione 4:

BasicDBObject dante = new BasicDBObject()
    .append("nome", "Dante")
    .append("cognome","Alighieri")
    .append("nato", 1265);
DBObject divinaCommedia = new BasicDBObject()
    .append("autore", dante)
    .append("allegati", new Binary(new byte[200]))
    .append("altroId", new ObjectId())
    .append("tags", new String[] {"poesia"});

Come si vede, un documento BSON deve essere una istanza di BasicDBObject; nella versione 3 del driver è stata introdotta la classe Document, che implementa java.util.Map e con la quale si può quindi usare anche il metodo put.

L’impostazione delle proprietà è simile, appunto, al riempimento di una mappa.

Operazioni CRUD

Esattamente come i comandi shell, le operazioni vanno effettuate sulle collezioni di un database. Per prima cosa bisogna selezionare un database, e da quello selezionare la collection. Possiamo farlo così:

DB db = mongoClient.getDB( "bookShop" );
DBCollection coll = db.getCollection("libri");

Nella versione 3, la sintassi è leggermente diversa:

MongoDatabase db = mongoClient.getDatabase("bookShop ");
MongoCollection<Document> coll = db.getCollection("libri ");

L’inserimento di documenti nel database si effettua con il metodo insert dell’oggetto DBCollection. L’oggetto WriteResult contiene informazioni sull’esito dell’operazione. Eventuali errori vengono segnalati tramite una MongoException:

WriteResult result = coll.insert(divinaCommedia);
System.out.println("Id: " + divinaCommedia.get("_id"));
System.out.println("Update? " + result.isUpdateOfExisting());

La funzione aggiorna automaticamente il documento inserito, in questo caso aggiungendo il campo _id.

Si possono inserire più documenti con la stessa chiamata, aggiungendoli come ulteriori parametri:

coll.insert(divinaCommedia, promessiSposi, orlandoFurioso);

Nella versione 3 del driver, invece, ci sono le funzioni insertOne ed insertMany per inserire rispettivamente un solo documento oppure una lista.

Un overload dello stesso metodo permette di specificare il Write Concern, che abbiamo visto nella quinta lezione. I Write Concern sono codificati come membri singleton statici nella classe WriteConcern:

coll.insert(WriteConcern.ACKNOWLEDGED, divinaCommedia);

In questo caso, avendo utilizzato Acknowledged, il comando ritorna al client non appena il server primario ha ricevuto il messaggio e gestito la memoria. Nella versione 3, il Write Concern va specificato non più qui ma direttamente sul database, con il metodo MongoDatabase.withWriteConcern.

Gli aggiornamenti possono essere effettuati con il metodo DBCollection.update che funziona esattamente come il suo omonimo metodo della shell, oppure con il metodo DBCollection.save, che è molto utile perché se non trova il documento con l’id specificato lo crea:

divinaCommedia.put("parti", "3");
result = coll.save(divinaCommedia);

La cancellazione può essere effettuata con il metodo DBCollection.remove, specificando i criteri:

coll.remove(new BasicDBObject().append("_id", "558d351236fae2f799bb5997"));

API asincrona (versione 3)

Accanto alla API sincrona che abbiamo visto, la versione 3 del driver fornisce una API asincrona che può essere molto utile nelle applicazioni pensate con un paradigma di reactive programming, oppure nei casi in cui non si voglia bloccare un thread per effettuare un salvataggio, o semplicemente per avere un codice più parallelizzato o più leggibile.

Per prima cosa bisogna modificare le dipendenze in Maven:

<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-async</artifactId>
<version>3.0.2</version>

A questo punto, recuperiamo la collezione in un modo simile a come visto precedentemente (cambiano alcuni metodi e il package):

import com.mongodb.async.client.*;
MongoClient mongoClient = MongoClients.create();
MongoDatabase db = mongoClient.getDatabase("libri1" );
MongoCollection<Document> coll = db.getCollection("testCollection");

Il metodo insertOne, usando Java8, diventa così:

coll.insertOne(divinaCommedia, (Void result, Throwable thrwbl) -> {
        if(thrwbl != null)
            logger.log(Level.SEVERE, "Sì è verificato un errore", thrwbl);
});

Il client effettuerà l’inserimento in asincrono ed eseguirà la lambda, loggando eventuali errori. Ciò, oltre ad essere più leggibile di un try catch, è anche effettuato in asincrono, quindi il flusso del thread può andare avanti senza bloccare, ad esempio, l’interfaccia grafica.

Ti consigliamo anche