Java POJOs con MongoDB

De ChuWiki


Vimos un ejemplo básico de cómo conectar Java con MongoDB y las operaciones básicas CRUD usando como datos objetos org.bson.Document. Veamos ahora cómo hacerlo con clases normales Java, con sus atributos y sus métodos getter y setter. Lo que normalmente se conocen como POJOs (Plain Old Java Objects).

Aquí veremos solo las modificaciones respecto al código del enlace anterior, por lo que si no estás familiarizado con la conexión de Java con MongoDB y cómo hacer las operaciones básicas CRUD, deberías leer primero en enlace anterior. En cualquier caso, tienes el código completo de este ejemplo en MongoDBPojosSampleMain.java

Configurar MongoDB para uso automático de POJO: PojoCodecProvider[editar]

MongoDB usa documentos JSON. Desde Java, por defecto, esos documentos se representan con org.bson.Document, también conocidos como documentos BSON. Vienen a ser como un HashMap de Java donde hay parejas clave/valor. Sin embargo, usar un documento BSON es tan cómodo de usar en código como usar un objeto java normal. Es más fácil usar nuestras objectos de datos de Java de toda la vida con sus atributos y sus métodos setter y getter.

Para ello, debemos configurar el driver de MongoDB para que use este tipo de clases. La clase PojoCodecProvider nos permite hacerlo. El código para ello es el siguiente

PojoCodecProvider pojoCodecProvider = PojoCodecProvider.builder().automatic(true).build();
CodecRegistry codecRegistry = CodecRegistries.fromRegistries(
   MongoClientSettings.getDefaultCodecRegistry(), CodecRegistries.fromProviders(pojoCodecProvider));

La explicación de estas líneas es algo compleja. En PojoCodecProvider, CodecRegsitry y Codec tienes una explicación con más detalle. Aquí solo vamos a dar un resumen. Primero tres conceptos:

  1. Un Codec es una clase capaz de convertir un objeto java a un documento bson y viceversa.
  2. Un CodecRegistry es un almacen de Codec que MongoDB usará para obtener el Codec adecuado para una clase Java concreta.
  3. Un CodecProvider es un clase que instancia Codec adecuados para determinadas clases Java.

Así que los pasos que debemos dar son

  1. Creamos una instancia de PojoCodecProvider. Esta instancia, configurada con automatic(true) será capaz de crear un Codec para cualquier POJO.
  2. Creamos un CodecRegistry con todos los Codec necesarios. La llamada CodecRegistries.fromRegistries(...) crea un CodecRegistry con todos los Codec que tengan a su vez los CodecRegistry que le pasemos como parámetro. Y le pasamos dos CodecRegistry
    1. MongoClientSettings.getDefaultCodecRegistry() es el CodecRegistry que tiene MongoDB por defecto y cuyos Codec queremos conservar.
    2. CodecRegistries.fromProviders(pojoCodecProvider) convierte nuestro PojoCodecProvider en un CodecRegistry capaz de devolver Codec para cualquier POJO.

Al final de todo este proceso, tenemos un almacén de Codec (codecRegistry) y que contendrá todos los Codec por defecto de MongoDB y los Codec para los POJO.

Obtener la base de datos de MongoDB configurada para POJO[editar]

Una vez establecemos la conexión con MongoDB, podemos obtener/crear la base de datos concreta que nos interse con getDatabase(). Es en esta llamada donde podemos indicar qué CodecRegistry queremos usar.

// Obtener o crear una base de datos
MongoDatabase database = mongoClient.getDatabase("My_Data_Base").withCodecRegistry(codecRegistry);

Hemos añadido al final .withCodecRegistry(codecRegistry). Así que a partir de aqui, en las colecciones que obtengamos, seremos capaces de utilizar nuestros POJO en vez de Document.

Operaciones básicas con POJO en colecciones MongoDB[editar]

Los siguientes trozos de código hacen las inserciones, consultas, modificaciones y borrados usando POJO en vez de Document. Imagina que Person es nuestro POJO con los campos String name y int age

Obtener la colección para el POJO[editar]

// Obtener o crear una colección
MongoCollection<Person> collection = database.getCollection("my_collection", Person.class);

Fíjate que hemos puesto el nombre de la clase como segundo parámetro y obtenemos una MongoCollection<Person>, es decir, una colección de Person. En el código del tutorial anterior se hacía con Document.

Insertar el POJO[editar]

// Insertar algún dato en la colección
Person pojoData = new Person("Pedro", 22);
InsertOneResult insertOneResult = collection.insertOne(pojoData);

La inserción es sencilla. Instanciar y rellenar nuestro POJO. Luego insertarlo con collection.insertOne(pojoData). Por supuesto, se puede usar collection.insertMany() pasando una lista de instancias del POJO para hacer una inserción múltiple.

Consultar POJOs[editar]

// Consultar la colección
FindIterable<Person> allCollection = collection.find();
allCollection.forEach(person -> System.out.println(person));

La consulta también es sencilla, con el método find() como en el tutorial anterior, pero esta vez obtenemos un iterable de Person en vez de Document.

Si queremos filtrar resultados, la parte del filtro no cambia respecto al tutorial anterior. Eso sí, obtendremos Person como resultado en vez de Document

// Consultar por una key
Bson filter = Filters.eq("name", "Pedro");
FindIterable<Person> elementsFound = collection.find(filter);
elementsFound.forEach(person -> System.out.println(person));

Modificar POJOs[editar]

// Modificar un elemento de la colección
Bson newAge = Updates.set("age", 23);
UpdateResult updateOne = collection.updateOne(filter, newAge);

La modificación puede hacerse de forma similar al tutorial anterior, usando la clase Updates e indicando qué atributos concretos queremos cambiar, la edad en el ejemplo. Luego una llamada a updateOne() indicando el filtro que selecciona los POJO a cambiar y el update a realizar (newAge).

Pero usando POJO, tenemos una opción también interesante. Consiste en reemplazar el POJO completo.

// Reemplazar el POJO
Person newPedro = new Person("Pedro", 14);
collection.replaceOne(filter, newPedro);

Simplemente recreamos el POJO con los nuevos datos, o modificamos el que tuvieramos si estuviera ya instanciado en nuestro programa, y llamamos a replaceOne(). Pasamos el filtro para buscar a Pedro y le pasamos la nueva instancia del POJO.

Borrar POJOS[editar]

// Borrar por key
DeleteResult deleteResult = collection.deleteMany(filter);

El borrado es exactamente igual al que haciamos en el tutorial anterior con Document. Solo tenemos que pasar el filtro de los elementos que queremos borrar.

Algunos detalles sobre los POJO para usar en MongoDB[editar]

Para que todo esto funcione correctamente, hay algunos detalles a tener en cuenta.

  • El POJO debe llevar un constructor público o protegido sin parámetros.
  • El codec usa por defecto los getter y setter de los atributos de nuestro POJO. Aquellos atributos que no tengan getter y setter no serán guardados o recuperados de base de datos. Existe configuración para modificar este tipo de cosas. Para ello, se añadiría la llamada a conventions() a la hora de construir el PojoCodecProvier: PojoCodecProvider.builder().conventions(conventionsList).automatic(true).build()
  • Hay más configuración posible. Por ejemplo, en vez de automatic(true) para tratar cualquier POJO, podríamos registrar los POJOs concretos que nos intersen o indicar le paquete Java donde están nustros POJO.

Pero todo esto son detalles para otro tutorial.