Salta el contingut

5. MongoDB i Java

Anem a veure com accedir a MongoDB des dels nostres programes Java. Primerament, com vam veure a la unitat 2, estudiarem com accedir directament i desprès des de ORM.

5.1. Drivers

Com ja sabem, per connectar des de les nostres aplicacions a una base de dades necessitem un controlador o driver. MongoDB ofereix drivers oficials per a una multitud de plataformes, incloent C, C++, C#, NodeJS, Python, i per descomptat, Java, entre molts altres.

Focalitzant-nos en Java, MongoDB ens ofereix dos drivers:

  • El driver Java per a aplicacions síncrones.
  • El driver de Reactive Streams per al processament de Streams asíncrons.

Encara que actualment hi ha una tendència cap a la programació reactiva, treballarem amb el driver Java síncron per facilitar la comprensió i centrar-nos en l'accés real a les dades.

5.1.1. El driver Java

Utilitzant el MongoDB Driver per a Java podem connectar tant a una base de dades local o remota, com a un clúster de MongoDB Atlas. Aquest driver (MongoDB Java Driver) es pot trobar als repositoris Maven, i proporciona un gran nombre de classes i interfícies per facilitar el treball amb MongoDB des de Java.

En un projecte Gradle hauríem d'utilitzar:

Bash
implementation group: 'org.mongodb', name: 'mongo-java-driver', version: '3.12.10'

mentre que en un projecte Maven:

XML
1
2
3
4
5
<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongo-java-driver</artifactId>
    <version>3.12.10</version>
</dependency>

5.2. Connexió a una base de dades

Per connectar i comunicar-nos amb una base de dades necessitem un client. En el cas del driver de Java per a MongoDB, el client s'implementa a través de la classe MongoClient.

La classe MongoClient representa un conjunt de connexions a un servidor MongoDB. Aquestes connexions són segures per a fils, és a dir, diversos fils d'execució poden accedir-hi de manera segura.

La manera de crear instàncies de MongoClient és a través del mètode MongoClients.create(). A més, generalment, només necessitem una instància d'aquesta classe, fins i tot en aplicacions multi-fil. El mètode MongoClients.create pren com a argument una Connection String, amb el següent format simplificat (els paràmetres entre claudàtors són opcionals):

Bash
mongodb:// [usuari:contrasenya @] host[:port] /?opcions

Així, una manera d'obtenir, per exemple, una connexió al servidor local seria:

Java
String uri = "mongodb://localhost:27017";
MongoClient mongoClient = MongoClients.create(uri);

La classe MongoClient, entre altres, suporta els següents mètodes:

  • getDatabase(String name) → Obté una referència a una base de dades el nom de la qual es passa com a argument.
  • listDatabaseNames() → Obté una llista de Strings (interfície MongoIterable) amb els noms de les bases de dades del servidor.
  • close() → Tanca la connexió amb el servidor. Sempre s'ha de fer quan ja no es vagi a utilitzar.

5.2.1. MongoDatabase

El mètode getDatabase() de la classe MongoClient retorna una referència a un objecte que implementa la interfície MongoDatabase, que representa una connexió a una base de dades. Aquesta interfície defineix els següents mètodes:

  • getCollection(String name) → Obté una referència a la col·lecció.
  • listCollectionNames() → Obté una llista de Strings (interfície MongoIterable) amb els noms de les col·leccions de la base de dades.
  • listCollections() → Obté una llista de referències (MongoCollection) a les col·leccions de la base de dades.
  • createCollection(String name) → Crea una nova col·lecció amb el nom especificat a la base de dades.
  • drop() → Elimina la base de dades.

Aquí trobaràs un exemple de connexió i llistat de bases de dades i col·leccions d'un servidor donat:

Java
public MongoClient connectServer(){
  String uri = "mongodb://root:toor@localhost:27017";
  MongoClient mongoClient = MongoClients.create(uri);
  return mongoClient;
}

public void disConnect(MongoClient client){
  client.close();
}

public void getInfo(MongoClient client) {
  // get list of databases
  for (String dbname : client.listDatabaseNames()) {
      // Write its name
      System.out.println("Database: " + dbname);

      // Get a database reference
      MongoDatabase db = client.getDatabase(dbname);

      // Get database collections
      MongoIterable<String> colecciones = db.listCollectionNames();

      // show collections name
      for (String coleccion : colecciones) {
          System.out.println("\t\t > " + coleccion);
      }
  }
}

5.3. Consultes

El mètode getCollection() de MongoDatabase() ens proporciona una col·lecció de Document (MongoCollection<Document>), sobre la qual podrem realitzar consultes utilitzant el mètode find(). Aquest mètode, que ja coneixem del shell de MongoDB, ens permetrà filtrar documents basant-nos en certs criteris.

Aquests criteris s'expressen com a filtres (query filters en la documentació), i poden contenir diversos operadors de consulta sobre alguns camps que determinaran quins documents de la col·lecció s'inclouen com a resultats.

La classe Filter ens proporciona mètodes de fàbrica per realitzar aquestes consultes, de manera similar a com treballàvem amb el shell de MongoDB. Aquesta classe ens ofereix:

  • Consulta buida, amb Filters.empty().
  • Operadors de comparació: Per realitzar consultes basades en valors de la col·lecció:
    • Filters.eq(key, value), Filters.gt(key, value), Filters.gte(key, value), Filters.lt(key, value) o Filters.lte(key, value).
  • Operadors lògics: Per realitzar operacions lògiques sobre el resultat d'altres consultes: Filter.and(other_filters), Filter.or(other_filters), etc.
  • Operadors d'array: Permeten realitzar consultes basades en el valor o nombre d'elements d'un vector: Filters.size(vector, size).
  • Altres operadors, com Filter.exists() o Filter.regex(), per comprovar l'existència d'una clau o realitzar una cerca amb expressió regular.

A més dels filtres, també podrem incloure operacions d'agregació, a través del mètode aggregate() d'una instància de MongoCollection. Pots consultar la documentació sobre agregacions a la guia d'operacions d'agregació de MongoDB.

D'altra banda, l'API del driver de MongoDB també ens permet realitzar projeccions de camps utilitzant la classe Projections, que ofereix els mètodes Projections.fields(), Projections.include() o Projections.excludeID().

Exemple de cerca de pel·lícules d'un any donat, només 10 resultats:

Java
public void getPelisAnyo(MongoClient client, int anyo) {

    // get collection from database
    MongoDatabase db=client.getDatabase("mflix");

    // get documents from that colection
    MongoCollection<Document> colPelis = db.getCollection("movies");

    // And now, we apply a filter and limit
    FindIterable<Document> docsPelis = colPelis
            .find(Filters.eq("year", anyo))
            .limit(10);

    // we show it
    for (Document doc : docsPelis) {
        System.out.println(doc.toString());
    }
}

Recorda

La classe Document té diversos mètodes per treballar com a documents JSON a la unitat 1. Podem obtenir cada camp donada una clau, obtenint el seu valor.

Un altre exemple amb filtres i projeccions:

Java
public void getPelisEntre(MongoClient client, int anyo1, int anyo2) {

    // check anyo values
    if (anyo1>anyo2){
        int tmp=anyo1;
        anyo1=anyo2;
        anyo2=tmp;
    }

    // get document collection
    MongoDatabase db=client.getDatabase("mflix");
    MongoCollection<Document> colPelis = db.getCollection("movies");

    // Creamos el filtro
    Bson filter=Filters.and(
        Filters.gte("year", anyo1),
        Filters.lte("year", anyo2)
    );

    // Create projecction
    Bson projection=Projections
            .fields(Projections.include("title", "year"),
                    Projections.excludeId());

    // Run the filters
    FindIterable<Document> DocsPelis = colPelis
            .find(filter)
            .projection(projection);

    // Show the films
    for (Document doc : DocsPelis) {
        System.out.println(doc.toString());
    }
}