Ejemplo de MongoTemplate con Spring Boot

De ChuWiki


MongoTemplate es una de las clases que nos ofrece Spring Boot para trabajar contra una base de datos MongoDB. Las otras dos son MongoRepository y MongoClient. La primera es de más alto nivel, la segunda es la de nivel más bajo, MongoTemplate es la de nivel intermedio. Podemos usar MongoClient desde Spring Boot si MongoTemplate se nos queda coja para alguna operación concreta.

MongoTemplate nos ofrece métodos muy similares a los del driver MongoClient, pero nos ofrece además métodos útiles para poder hacer operaciones CRUD con objetos Java y facilitarnos la construcción de filtros en las consultas.

Veamos aquí un ejemplo de cómo usar MongoTemplate en Spring Boot. Tienes el código completo del ejemplo en la clase BeanWithMongoTemplate.java que forma parte del proyecto github spring-boot-mongodb

Para ejecutar el ejemplo necesitarás una base de datos MongoDB instalada en localhost, puerto por defecto 27017 y sin autentificacion de usuarios. No obstante, si tienes una instalación de MongoDB diferente, puedes tocar el fichero src/main/resources/application.properties del proyecto para poner los parámetros de conexión a tu base de datos MongoDB.

Código base para el ejemplo de MongoTemplate con MongoDB[editar]

Veamos algunas cosas previas que tenemos que crear para poder hacer los ejemplos. Todo el código está en el enlace de github que se ha mencionado más arriba.

Creación de un POJO para insertar en MongoDB[editar]

Lo primero que necesitamos es una clase Java, un POJO (Plain Old Java Object) que contenga los datos que queremos insertar en MongoDB. En los dos siguientes enlaces tienes cómo se ha creado dicho POJO para este ejemplo

Inyección de MongoTemplate en Spring Boot[editar]

Spring Boot en el arranque crea una instancia de MongoTemplate que está conectada con la base de datos MongoDB. Es importante saber que Spring Boot sólo instancia una vez MongoTemplate, que por defecto está conectado a un único servidor de MongoDB y una base de datos concreta de MongoDB dentro de ese servidor. Hay formas de hacer que Spring Boot haga más instancias de MongoTemplate para conectarse a distintas bases de datos, usando clases con la anotación @Configuration de Spring Boot, pero tendríamos que hacer el código específico para ello.

Necesitamos que nuestro código reciba por inyección de dependencias la instancia de MongoTemplate. Para ello, debemos crear una clase Java nuestra propia que sea Spring Boot el encargado de instanciar y a la que inyecte la instancia de MongoTemplate. Eso se consigue con un código como el siguiente

@Component
public class BeanWithMongoTemplate {

    /** MongoTemplate lo inyecta Spring Boot */
    @Autowired
    MongoTemplate mongoTemplate;

    /**
     * Comenzamos a hacer alguna operación CRUD con MongoDB cuando el contexto esté inicializado.
     * Lo hacemos en un método @PostConstruct para que comience el ejemplo de forma automatica,
     * sin necesidad de crear una interface de usuario o web services a los que haya que invocar para
     * provocar la ejecución del ejemplo.
     */
    @PostConstruct
    public void doCRUDOperations(){
       ...
    }

    ...
}

Hemmos creado la clase BeanWithMongoTemplate. Esta clase tiene la anotación @Component, de forma que Spring Boot la instanciará en el arranque. Y le hemos puesto un atributo MongoTemplate, anotado con @Autowired. Esto hará que Spring Boot inyecte en este atributo la instancia de MongoTemplate.

Para nuestro ejemplo vamos a hacer que el código que demuestra cómo trabajar con MongoTemplate comience automáticamente. Ponemos en la clase BeanWithMongoTemplate un método anotado con @Postconstruct. Spring Boot llamará a este método en cuanto nuestra clase BeanWithMongoTemplate esté instanciada y le haya inyectado todas las dependencias, en nuestro caso, la instancia MongoTemplate.

Ejemplos de operaciones CRUD con MongoTemplate[editar]

Ahora sí, vamos con los ejemplos de operaciones de inserción, consulta, modificación y borrado con MongoTemplate

Verificar la base de datos MongoDB a la que está conectado MongoTemplate[editar]

Como primer paso, podemos verificar a qué base de datos de MongoDB está conectado MongoTemplate. El método getDb() nos da esta información

MongoDatabase database = mongoTemplate.getDb();
System.out.println("Base de Datos por defecto para MongoTemplate "+database.getName());

Si no hemos tocado el fichero appllication.properties, la configuración será la de por defecto de Spring Boot y la base de datos se llamará "test"

Insertar objetos java con MongoTemplate insert(), insertAll() y save()[editar]

Para insertar objetos Java en MongoDb, MongoTemplate nos ofrece varios métodos. El método save() tiene la misma sintaxis que el del driver MongoClient. Está ahí para que alguien acostumbrado a usar MongoClient no encuentre dificultades al pasarse a MongoTemplate.

Los métodos insert() e insertAll() son los específicos de MongoTemplate para inserciones de objetos Java. Veamos un ejemplo

List<Person> persons = new ArrayList<>();
persons.add(new Person("Pedro", 1.70));
persons.add(new Person("Monica", 1.90));
Collection<Person> people = mongoTemplate.insertAll(persons);
System.out.println("Insertado "+ Arrays.toString(people.toArray()));

Hemos creado una lista de personas y usado el método insertAll() para guardarlas en MongoDB. La colección en la que se guardarán se deduce del tipo de la lista (Person), así que la colección de MongoDB será "person". Podemos verificarlo con el siguiente código

System.out.println("Colecciones : "+ Arrays.toString(mongoTemplate.getCollectionNames().toArray()));

El método getCollectionNames() nos devuelve los nombres de las colecciones que hay en la base de datos MongoDB. Deberíamos ver la colección "person".

Los métodos insert() permiten hacer lo mismo, insertar una lista de objetos java, pero nos permiten elegir el nombre de la colección donde queremos guardarlo como segundo parámetro. Puede ser el String con el nombre de la colección o una clase Java cuyo nombre es el que se dará a la colección.

Consultar objetos Java con MongoTemplate findAll()[editar]

Para la consulta de todos los objetos de una colección, tenemos el método findAll(). Le pasasmos como parámetro el nombre de la clase Java que queremos obtener de la consulta y opcionalmente un segundo parámetro con el nombre de la colección en MongoDB. Si no ponemos este segundo parámetro, el nombre de la colección MongoDB se deducirá del nombre de la clase Java que pasamos como primer parámetro.

// Consulta de todos los POJO insertados
final List<Person> all = mongoTemplate.findAll(Person.class);
System.out.println("Personas : "+ Arrays.toString(all.toArray()));

Consultar objetos Java con MongoTemplate, find(), Query y Criteria[editar]

Si queremos consultar pasando algún tipo de filtro de búsqueda, tenemos las clase Query y Criteria. Query es la consulta que queremos hacer y Criteria es cada una de las condiciones de Query. Por ejemplo, un Criteria es que el nombre de Person sea "Pedro". Con uno o más Criteria hacemos la Query. Veamoslo con un ejemplo

// Consulta con query de POJO insertados
Query query = Query.query(Criteria.where("name").is("Pedro"));
Collection<Person> people = mongoTemplate.find(query, Person.class);
people.forEach(personFound -> System.out.println("Query : "+personFound));

Construimos la Query con Query.query(...). Pasamos como parámetros los Criteria que construimos con Criteria.where(). En el código puede ver que el Criteria es que "name" sea "Pedro". Una vez tenemos la query, la consulta se hace con mongoTemplate.find(). Pasamos de parámetro la Query y el nombre de la clase que esperamos obtener en la consulta.

Criteria nos permite también ir añadiendo condiciones AND y OR para hacer Query más complejas.

query = Query.query(Criteria.where("name").is("Monica").andOperator(Criteria.where("height").gt(1.90)));

Y entre las operaciones, además de las típicas eq() (igual), gt() (mayor o igual que), etc, tenemos otras como si el atributo está vacío o existe, de coordendas geográficas, etc.

Consultar objetos java con MongoTemplate by example[editar]

Criteria también tiene como alternativa a where() el método byExample(). En esté método le pasamos una instancia del objeto Java que estamos buscando, pero que solo tiene rellenos aquellos atributos que nos interesan para la búsqueda. Veamos algo de código

// Consulta con byExample
Person examplePerson = new Person("Pedro",null);
query = Query.query(Criteria.byExample(Example.of(examplePerson)));
people = mongoTemplate.find(query, Person.class);
people.forEach(personFound -> System.out.println("Query Sample : "+personFound));

Hemos creado una examplePerson con solo el nombre relleno, "Pedro", pero no la altura que dejamos a null. Construimos la Query de la forma habitual, pero en Criteria usamos el método byExample() en vez de where(). Y le pasamos como parámetro el resultado de Example.of(examplePerson). Haciendo la consulta obtendremos las personas de nombre "Pedro".

Example.of() admite un segundo paráemtro donde podemos afinar cómo debe coincidir la persona de muestra con lo que hay en la colección MongoDB. Por ejemplo, si nuestra persona de ejemplo tiene rellenos dos atributos, podemos decir que coincida cualquiera de ellos o que concidan los dos. O si el objeto Java que hay guardado en la colección no es un Person, pero tiene los atributos de la persona ejemplo, podemos indicar si queremos ese resultado o no. Veamos un ejemplo del primer caso

// Consulta by example mezclando atributos.
examplePerson = new Person("Pedro", 1.90);  // Nombre Pedro, altura de Mónica.
query = Query.query(Criteria.byExample(Example.of(examplePerson, ExampleMatcher.matchingAny())));
people = mongoTemplate.find(query, Person.class);
people.forEach(personFound -> System.out.println("Query Sample matching any : "+personFound));

Hemos creado una persona de ejemplo con el nombre de "Pedro", pero la altura de "Mónica". A la hora de constuir el Example.of(), hemos pasado un segundo parámetro ExampleMatcher.matchingAny(). Con esto indicamos que nos vale que coincida cualquiera de los dos atributos. El resultado es que obtendremos en la consulta a "Pedro" y a "Mónica".

Modificación de objetos Java con MongoTemplate updateFirst(), updateMulti() y upsert()[editar]

Para la moficación de objetos Java ya salvados en la colección de MongoDB, tenemos la clase Update que nos permite hacer modificaciones. Veamos el ejemplo

// Update de un POJO
query = Query.query(Criteria.where("height").gt(1.80));
Update update = new Update().update("height", 2.0);
mongoTemplate.updateFirst(query, update, Person.class);

Primero tenemos que construir una query para seleccionar de la colección MongoDB aquellos objetos Java que queremos modificar. Después creamos una instancia de la clase Update y usando sus métodos decimos qué queremos modificar. En nuestro ejemplo, hacemos un set() de "height" a 2.0. Hay muchos más métodos, para borrar el campo, para incrementarlo o decrementarlo, para ponerle una fecha actual, etc, etc.

Finalmente, llamamos a updateFirst() si solo queremos que se modifique el primer resultado de la Query. Llamamos a updateMultiple() si queremos que se modifiquen todos los resultados de la Query. O bien a upsert(), si queremos que en caso de que no exista ningun objeto que cumpla la Query, se inserte uno con los datos que le hemos pasado. Veamos el ejemplo con upsert()

// Upsert de un Pojo
query = Query.query(Criteria.where("name").is("Juan"));
update = new Update().set("height", 1.51);
mongoTemplate.upsert(query, update, Person.class);

Buscamos un "Juan" al que queremos modificar la altura y ponerle 1.51. Como no tenemos un "Juan" en nuestra colección, upsert() no lo encontrará y entonces insertará una persona "Juan" de altura 1.51

Borrados de objetos Java con MongoTemplate remove(), findAndRemove() y findAllAndRemove()[editar]

El método remove() permite borrar por la clave primaria del objeto, es decir, el atributo que lleve la anotación @Id, o bien por una Query y borrar todos los objetos que la cumplan. Este método devuelve un resultado del borrado indicando cuántos objetos Java se han borrado de la colección.

findAndRemove() busca el primer objeto que cumpla la query, lo borra y nos lo devuelve como resultado del método. findAllAndRemove() hace lo mismo, pero borra y devuelve todos los objetos Java que cumplan la Query.

Veamos algo de código

// Borrado de un POJO
query = Query.query(Criteria.where("name").is("Pedro"));
Person personRemoved = mongoTemplate.findAndRemove(query, Person.class);
System.out.println("Deleted : "+personRemoved);

Hemos creado la Query y llamado a findAndRemove(). El segundo parámetro es la colección MongoDB donde está el objeto a borrar, o bien la clase a partir de la cual se deduce el nombre de la colección.

Eliminar la colección MongoDB con MongoTemplate dropCollection()[editar]

Podemos eliminar la colección completa con dropCollection(). Esto elimina todos los objetos de la colección y elimina la colección de la base de datos MongoDB. Solo tenemos que pasar como parámeto el nombre de la colección o la clase a partir de la cual se deduce el nombre de la colección.

// Borrado de la base de datos MongoDB completa
mongoTemplate.dropCollection(Person.class);