Salta el contingut

MongoDB des de Java

1. Què és MongoDB?

MongoDB és una base de dades documental: en lloc de guardar dades en taules amb files i columnes, guarda documents en format similar a JSON (concretament BSON, Binary JSON).

Això la fa molt flexible: cada document d'una col·lecció pot tenir camps diferents, i els camps poden contenir valors simples, arrays o documents imbricats.

1.1. Comparació amb MySQL

Concepte MySQL Concepte MongoDB
Base de dades Base de dades
Taula Col·lecció (collection)
Fila Document
Columna Camp (field)
Clau primària _id
JOIN $lookup (o documents imbricats)

1.2. Exemple de document

Un estudiant a MongoDB es pot representar així:

JSON
{
  "_id": "ObjectId('64a1b2c3...')",
  "nom": "Ana Pérez",
  "edat": 22,
  "cicle": "Enginyeria de Sistemes",
  "contacte": {
    "email": "ana.perez@uni.edu",
    "telefon": "555-1234"
  },
  "moduls": [
    { "nom": "Bases de Dades", "credits": 4, "nota": 8.5 },
    { "nom": "Programació Avançada", "credits": 5, "nota": 9.0 }
  ]
}

Observeu que un document pot contenir documents imbricats (contacte) i arrays (moduls). Això és molt diferent del model relacional.

Disposes de l'arxiu estudiants.json amb exemples de documents d'estudiants per practicar a les nostres consultes MongoDB.

2. Configuració: El Driver de MongoDB

Afegiu la dependència al vostre pom.xml:

XML
1
2
3
4
5
<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongodb-driver-sync</artifactId>
    <version>4.11.1</version>
</dependency>

Les classes principals que usarem:

Classe Descripció
MongoClient Gestiona la connexió amb el servidor MongoDB
MongoDatabase Representa una base de dades
MongoCollection<Document> Representa una col·lecció de documents
Document Representa un document BSON/JSON
Filters Classe d'utilitat per construir filtres de cerca
Updates Classe d'utilitat per construir operacions d'update

3. Connexió a MongoDB

Java
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;

// La URI de connexió estàndard de MongoDB
// Format: mongodb://host:port
MongoClient client = MongoClients.create("mongodb://localhost:27017");

// Seleccionem la base de dades (es crea automàticament si no existeix)
MongoDatabase db = client.getDatabase("escola");

// Seleccionem la col·lecció (equivalent a una taula)
MongoCollection<Document> col = db.getCollection("estudiants");

Creació automàtica

A diferència de MySQL, MongoDB crea automàticament la base de dades i la col·lecció quan hi inserim el primer document. No cal executar cap CREATE DATABASE ni CREATE TABLE.

4. La Classe Document

Document és la classe central per treballar amb MongoDB en Java. Funciona com un mapa clau-valor i és la representació Java d'un document JSON/BSON.

Java
// Crear un document nou
Document estudiant = new Document();
estudiant.append("nom", "Joan Garcia");
estudiant.append("edat", 21);
estudiant.append("cicle", "DAM");

// Alternativa: encadenar append() directament
Document estudiant2 = new Document("nom", "Maria Puig")
        .append("edat", 20)
        .append("cicle", "DAW");

// Llegir valors d'un document
String nom  = estudiant2.getString("nom");
int    edat = estudiant2.getInteger("edat");

5. READ: Consultar Documents

5.1. Obtenir tots els documents

Java
import com.mongodb.client.*;
import org.bson.Document;

public class LlistarEstudiants {
    public static void main(String[] args) {

        // try-with-resources: tanca el MongoClient automàticament
        try (MongoClient client = MongoClients.create("mongodb://localhost:27017")) {

            MongoDatabase db  = client.getDatabase("escola");
            MongoCollection<Document> col = db.getCollection("estudiants");

            // find() sense arguments retorna tots els documents
            // Iterem amb un bucle for-each
            for (Document doc : col.find()) {
                String nom   = doc.getString("nom");
                int    edat  = doc.getInteger("edat");
                String cicle = doc.getString("cicle");

                System.out.printf("%s, %d anys - %s%n", nom, edat, cicle);
            }
        }
        // client es tanca automàticament aquí
    }
}

5.2. Filtrar documents amb Filters

La classe Filters proporciona mètodes estàtics per construir condicions de cerca:

Java
import com.mongodb.client.model.Filters;
import org.bson.conversions.Bson;

// Igualtat: cicle = "DAM"
Bson filtre = Filters.eq("cicle", "DAM");

// Més gran que: edat > 21
Bson filtre = Filters.gt("edat", 21);

// Menys o igual: edat <= 22
Bson filtre = Filters.lte("edat", 22);

// Conté text (regex): nom que conté "Pe"
Bson filtre = Filters.regex("nom", "Pe");

// Condició AND: cicle = "Enginyeria de Sistemes" I edat <= 22
Bson filtre = Filters.and(
    Filters.eq("cicle", "Enginyeria de Sistemes"),
    Filters.lte("edat", 22)
);

5.3. Exemple: Buscar estudiants per cicle

Java
import com.mongodb.client.*;
import com.mongodb.client.model.Filters;
import org.bson.Document;
import org.bson.conversions.Bson;

public class BuscarPerCicle {
    public static void main(String[] args) {

        try (MongoClient client = MongoClients.create("mongodb://localhost:27017")) {

            MongoCollection<Document> col = client.getDatabase("escola")
                                                  .getCollection("estudiants");

            // Filtre: cicle igual a "Enginyeria de Sistemes"
            Bson filtre = Filters.eq("cicle", "Enginyeria de Sistemes");

            System.out.println("Estudiants d'Enginyeria de Sistemes:");
            System.out.println("=".repeat(40));

            // find(filtre) aplica el filtre a la cerca
            for (Document doc : col.find(filtre)) {
                System.out.println("Nom: " + doc.getString("nom"));
                System.out.println("Edat: " + doc.getInteger("edat"));

                // El camp 'contacte' és un document imbricat
                Document contacte = (Document) doc.get("contacte");
                if (contacte != null) {
                    System.out.println("Email: " + contacte.getString("email"));
                }
                System.out.println();
            }
        }
    }
}

5.4. Obtenir un sol document

Java
1
2
3
4
5
6
7
8
// first() retorna el primer document que coincideix, o null si no n'hi ha cap
Document doc = col.find(Filters.eq("nom", "Ana Pérez")).first();

if (doc != null) {
    System.out.println("Trobat: " + doc.getString("nom"));
} else {
    System.out.println("No s'ha trobat l'estudiant.");
}

6. CREATE: Inserir Documents

6.1. Inserir un document

Java
import com.mongodb.client.*;
import org.bson.Document;

public class AfegirEstudiant {
    public static void main(String[] args) {

        try (MongoClient client = MongoClients.create("mongodb://localhost:27017")) {

            MongoCollection<Document> col = client.getDatabase("escola")
                                                  .getCollection("estudiants");

            // Construïm el document de l'estudiant nou
            // amb camps simples, un document imbricat i un array
            Document nouEstudiant = new Document("nom", "Pau Martí")
                .append("edat", 19)
                .append("cicle", "DAM")
                // Document imbricat per a les dades de contacte
                .append("contacte", new Document("email", "pau.marti@institut.cat")
                                        .append("telefon", "666-111-222"))
                // Array de mòduls (llista de documents)
                // Nota: les notes són double, entre 0 i 10
                .append("moduls", java.util.Arrays.asList(
                    new Document("nom", "Programació").append("credits", 5).append("nota", 7.8),
                    new Document("nom", "Bases de Dades").append("credits", 4).append("nota", 8.2)
                ));

            // insertOne() insereix un sol document
            col.insertOne(nouEstudiant);

            // Després d'inserir, MongoDB assigna un _id automàticament
            System.out.println("Estudiant inserit amb _id: " + nouEstudiant.getObjectId("_id"));
        }
    }
}

6.2. Inserir múltiples documents

Java
1
2
3
4
5
6
7
8
9
import java.util.*;

List<Document> estudiants = new ArrayList<>();
estudiants.add(new Document("nom", "Clara Vidal").append("edat", 20).append("cicle", "DAW"));
estudiants.add(new Document("nom", "Marc Soler").append("edat", 22).append("cicle", "ASIX"));

// insertMany() insereix una llista de documents d'una sola vegada
col.insertMany(estudiants);
System.out.println("S'han inserit " + estudiants.size() + " estudiants.");

7. UPDATE: Modificar Documents

La classe Updates proporciona mètodes per construir les operacions de modificació:

Java
import com.mongodb.client.model.Updates;
import com.mongodb.client.model.Filters;

// Actualitzar un camp simple: canviar el cicle
col.updateOne(
    Filters.eq("nom", "Ana Pérez"),    // filtre: quin document
    Updates.set("cicle", "DAW")        // operació: quin canvi
);

// Incrementar un valor numèric: edat + 1
col.updateOne(
    Filters.eq("nom", "Luis Martínez"),
    Updates.inc("edat", 1)
);

// Combinar múltiples canvis en un sol update
col.updateOne(
    Filters.eq("nom", "María López"),
    Updates.combine(
        Updates.set("edat", 24),
        Updates.set("contacte.email", "maria.lopez.nou@uni.edu")
    )
);

7.1. Exemple: Actualitzar la nota d'un mòdul

Java
import com.mongodb.client.*;
import com.mongodb.client.model.*;
import org.bson.Document;
import org.bson.conversions.Bson;

public class ActualitzarNota {
    public static void main(String[] args) {

        try (MongoClient client = MongoClients.create("mongodb://localhost:27017")) {

            MongoCollection<Document> col = client.getDatabase("escola")
                                                  .getCollection("estudiants");

            // Filtre: volem modificar "Ana Pérez"
            Bson filtre = Filters.eq("nom", "Ana Pérez");

            // Operació: canviar la nota (double) del primer mòdul de l'array
            // La notació "moduls.0.nota" accedeix a l'índex 0 de l'array
            Bson update = Updates.set("moduls.0.nota", 9.5);

            // updateOne() modifica el primer document que coincideix amb el filtre
            var resultat = col.updateOne(filtre, update);

            System.out.println("Documents trobats: " + resultat.getMatchedCount());
            System.out.println("Documents modificats: " + resultat.getModifiedCount());
        }
    }
}

8. DELETE: Eliminar Documents

Java
import com.mongodb.client.*;
import com.mongodb.client.model.Filters;
import org.bson.conversions.Bson;

public class EliminarEstudiant {
    public static void main(String[] args) {

        try (MongoClient client = MongoClients.create("mongodb://localhost:27017")) {

            MongoCollection<Document> col = client.getDatabase("escola")
                                                  .getCollection("estudiants");

            // Eliminem l'estudiant amb nom "Pau Martí"
            Bson filtre = Filters.eq("nom", "Pau Martí");

            // deleteOne() elimina el primer document que coincideix
            var resultat = col.deleteOne(filtre);

            System.out.println("Documents eliminats: " + resultat.getDeletedCount());
        }
    }
}

Per eliminar tots els documents que compleixen un criteri:

Java
1
2
3
// deleteMany() elimina tots els documents que coincideixen
var resultat = col.deleteMany(Filters.lt("edat", 18));
System.out.println("Eliminats: " + resultat.getDeletedCount());

9. Accedir a Documents Imbricats i Arrays

Una de les característiques de MongoDB és poder tenir dades estructurades dins el propi document.

Java
Document doc = col.find(Filters.eq("nom", "Ana Pérez")).first();

if (doc != null) {
    // Camp simple
    String nom = doc.getString("nom");

    // Document imbricat: el camp 'contacte' és un Document
    Document contacte = doc.get("contacte", Document.class);
    String email = contacte.getString("email");

    // Array de documents: el camp 'moduls' és una List<Document>
    List<Document> moduls = doc.getList("moduls", Document.class);

    System.out.println("Estudiant: " + nom);
    System.out.println("Email: " + email);
    System.out.println("Mòduls:");

    for (Document modul : moduls) {
        String nomModul = modul.getString("nom");
        int    credits  = modul.getInteger("credits");
        // La nota és un double (0.0 - 10.0)
        double nota     = modul.getDouble("nota");
        System.out.printf("  - %-40s (%d crèdits): %.1f%n", nomModul, credits, nota);
    }
}

getDouble() vs getInteger()

Les notes s'emmagatzemen com a double al document JSON (8.5, 4.2...). Per llegir-les correctament cal usar modul.getDouble("nota") i no getInteger(), ja que aquest últim retornaria null per a valors amb decimals.

10. Comparativa Final: MySQL vs MongoDB

Aspecte MySQL (JDBC) MongoDB (Driver)
Connexió DriverManager.getConnection() MongoClients.create()
Consulta executeQuery()ResultSet find()MongoCursor
Filtre Codi SQL (WHERE ...) Filters.eq(...)
Inserció executeUpdate() INSERT insertOne() / insertMany()
Modificació executeUpdate() UPDATE updateOne() / updateMany()
Eliminació executeUpdate() DELETE deleteOne() / deleteMany()
Esquema Rígid (definit a CREATE TABLE) Flexible (cada doc pot variar)
Relacions Claus forànies + JOIN Documents imbricats o $lookup
Excepcions SQLException MongoException

Quan usar cadascun?

  • MySQL: Dades estructurades amb relacions clares, moltes consultes complexes, necessitat de transaccions ACID.
  • MongoDB: Dades flexibles o variables, documents complexos amb arrays i sub-documents, prototipat ràpid.