MongoDB con Java/Spring

MongoDB es una base de datos NoSQL cuyo uso ha ido aumentando a lo largo de los últimos años. Recientemente tuve la oportunidad de usarla con Java, por eso me pareció una buena idea resumir los principales puntos que surgieron con el uso.

El objetivo principal es mostrar, mediante un paneo general los principales aspectos del uso de este motor de bases de datos. Veremos ejemplos concretos de inserción, modificación y consulta de documentos (lo que equivale a  registros en términos de bases de datos transaccionales).

Primero que nada, veamos como agregar las dependencias necesarias en nuestro proyecto. Para este ejemplo, utilizamos Maven para incluir el driver de mongo y spring data, pero para proyectos que no cuentan con esta herramienta, simplemente es agregar el archivo jar de SpringData:



  org.mongodb
  mongo-java-driver
  2.10.1


  org.springframework.data
  spring-data-mongodb
  1.2.0.RELEASE
  

  

Ahora pasamos a la parte más interesante, la utilización de mongoDB. Por lo general, tenemos un DAO que va a envolver un objeto de tipo MongoTemplate, el cual se configura para conectarse al motor de mongo que querramos, similar a lo que lo haría un EntityManager tradicional (para bases de datos transaccionales). Veamos una posible configuración:

    
    

Y luego, tenemos el siguiente DAO, que hace uso del objeto mongoTemplate declarado en el fragmento de ApplicationContext.xml mostrado en el punto anterior:

@Repository
public class MongoAdminDao implements AdminDao {

    @Autowired
    private MongoTemplate mongoTemplate;

    // ...
}

Con Spring, podemos realizar el mapeo de documentos (Registros) con la anotación @Document (equivalente a la anotación @Entity en Java Persistence API):

@Document(collection = "admins")
public class Admin {

    @Indexed
    private Integer adminId;
    // ...
}

Ahora pasemos a un insert sencillo. Para esto solo necesitamos especificar el objeto que queremos insertar, y el framework se encarga de inferir a qué colección (el equivalente a tablas en bases de datos relacionales) agregar el objeto:

public void insert(Admin admin) {
    mongoTemplate.insert(admin);
}

Sencillo, no? Ahora pasemos a la modificación, que es un poco más compleja.

Para esto, necesitamos describir cómo se va a realizar el mapeo de los campos a los documentos. Esto es así porque mongoDB es bastante flexible, por lo que si especificamos un campo que no existe en los documentos, se va a agregar como parte de la modificación. Para poder especificar este mapeo, utilizamos un objeto Update, que tiene un conjunto de campos (que queremos que se modifiquen) y desde donde leer los valores que van a ser guardados:

public void upsert(Admin admin) {
    Query query = new Query(Criteria.where(ADMIN_ID).is(admin.getAdminId()));
    Update update = toUpdate(admin);
    maongoTemplate.upsert(query, update, Admin.class);
}

private Update toUpdate(Admin admin) {
    Update update = new Update();
    update.set(ADMIN_ID, admin.getAdminId());
    update.set(PARTNER_ID, admin.getPartnerId());
    update.set(PARTNER_NAME, admin.getPartnerName());
    update.set(NAME, admin.getName());
    // ...
    return update;
}

En este caso hicimos un upsert que es algo distinto al update. Un upsert es un insert or update, por lo que si el objeto que queremos modificar existe en la base de datos, se realiza un update, caso contrario se hace un insert.

Para poder realizar el upsert el template necesita una Query que filtre aquellos objetos que deben ser modificados. En este ejemplo, estamos modificando aquellos Documentos de la colección Admin (Admin.clas), que tengan el campo ADMIN_ID igual al id que es pasado como parámetro.

Pasemos al último punto del post, las consultas. Básicamente, construiremos un objeto Query con distintos filtros. Lo que quiero destacar de este apartado es cómo podemos generar un Criterio de manera dinámica, algo que puede ser útil a la hora de búsquedas parametrizadas:

public List searchAllByPartnerId(Integer partnerId,
                                        String name,
                                        String partnerName) {
Criteria searchCriteria =
buildSearchCriteriaByPartnerId(partnerId, name, partnerName);
Query query = new Query(searchCriteria);
return mongoTemplate.find(query, Admin.class);
}

private Criteria buildSearchCriteriaByPartnerId(Integer partnerId,
String name,
String partnerName) {

int size = getAmountOfNonBlankStrings(name, partnerName);

Criteria

Criteria.where() nos permite seleccionar el campo sobre el cual queremos aplicar una comparación, y regex() es una búsqueda utilizando expresiones regulares (similar al LIKE de mysql). Nótese la diferencia con Criteria.where().is(), el cual realiza una búsqueda por igualdad. En este caso en particular, el partnerId siempre va a estar especificado, por eso es agregado directamente al arreglo de partialAnds. Los campos name y partnerName pueden no estar presentes, pero si están, se genera una expresión de búsqueda  con expresión regular, para encontrar aquellos documentos que contienen la cadena dada.

Conclusiones

La utilización de mongoDB es bastante simple con Spring, la documentación es extensa y se puede encontrar ayuda en distintos tutoriales.

Algo en particular que considero es un problema grande que tiene el motor de base de datos, es que no soporta ordenar registros de forma «case insensitive». Se puede salvar esta situación si se realiza una consulta con agregaciones, que lo que hace básicamente es copiar el campo por el cual se quiere ordenar en otro campo temporal y luego ordena por ese valor (normalizado, es decir reescrito todo en minúsculas o en mayúsculas); pero obviamente esto está lejos de ser óptimo. Además de que el framework de Spring está preparado para realizar el ordenamiento, por lo cual se puede decir que estamos simplemente esperando que MongoDB lo soporte.

Fuera de ese problema, no me tomó demasiado tiempo aprender a utilizar tanto MongoDB como SpringData, por lo cual lo recomiendo, ya que agrega simplicidad al desarrollo.

Les dejo algunos enlaces interesantes:

Get in Touch