Spring Data MongoDB: Implementación de un repositorio a medida


Introducción

Spring Data facilita mucho el proceso de trabajo con entidades de datos y ofrece una implementación específica para MongoDB. Se pueden definir consultas simplemente creando interfaces con métodos que siguen una convención de nombres o anotándolos con @Query y Spring automágicamente generará una implementación por nosotros. En la mayoría de ocasiones esto nos bastará para operaciones CRUD o de consultas sencillas y no será necesario definir métodos adicionales. Esto nos permitirá finalizar el desarrollo de la aplicación o de la funcionalidad de acceso a datos de forma muy rápida sin tener que escribir código repetitivo.

Sin embargo, en muchas ocasiones esto no será suficiente y la interfaz que define el repositorio necesitará disponer de métodos no estándar e implementaciones específicas que nos permitirán dar respuesta a funcionalidades menos estándar.

En esta publicación veremos como desarrollar estas implementaciones a medida para un repositorio de libros en MongoDB y cómo definir tests unitarios que probarán dicha implementación a medida.

Definiendo la Entidad/Documento

El primer paso será definir el Documento principal de MongoDB que se devolverá en las consultas. Para el caso del ejemplo de este tutorial será un documento definido dentro de la clase Book con la información básica de la publicación de un libro.

Las partes más importantes de esta clase son la anotación @Document que indica que esta clase representa un documento dentro de la colección “books” de MongoDB, y la anotación @Id que indica a Spring que este campo debería de emplearse como el identificador único del documento/entidad.

The main parts of this class are the @Document annotation that indicates that this class represents a MongoDB Document  of the books collection, and the @Id annotation which tells Spring that this field should be used as the unique identification for the document.

Tal como se ha comentado, esta entidad contendrá información básica (título, número ISBN, nombres de autor, etc.) de un libro.

Definiendo el repositorio base

El siguiente paso es definir la interfaz base para el repositorio de libros.

El repositorio extiende la interfaz MongoRepository, indicando a Spring que se trata de un repositorio específico de MongoDB, heredando todos los métodos disponibles en las interfaces padre (PagingAndSortingRepositoryCrudRepository…).

Añadiremos un método findByTitleContainingOrderByTitle a la interfaz para mostrar como Spring auto-implementará este método sin necesidad de hacerlo en nuestra implementación a medida.

Definiendo los métodos personalizados del repositorio a medida

Para este tutorial queremos añadir la capacidad de hacer consultas dinámicas sobre la colección de libros. Con este propósito definiremos una clase DynamicQuery que contendrá los campos opcionales para los que se quiere poder filtrar la colección.

A continuación definiremos una interfaz que consuma un objeto tipo DynamicQuery y que devuelva una lista de Books. Esta interfaz se llamará BookRepositoryCustom, Es muy importante seguir la convención de nombres para que Spring pueda instanciar la implementación específica de esta nueva interfaz.

Implementando los métodos del repositorio a medida

Es muy importante seguir la convención de nombres si queremos que Spring detecte nuestras implementaciones customizadas. Por defecto Spring escaneará el paquete donde se define la interfaz a medida y los paquetes por debajo de éste para encontrar una clase que extienda dicha interfaz. Si la configuración para la convención de nombres no se ha modificado, Spring por defecto buscará una clase con un sufijo “Impl” en el nombre. Por tanto, crearemos una clase BookRepositoryImpl que implemente la interfaz a medida BookRepositoryCustom.

Esta clase se comportará como cualquier otro Bean de Spring por lo que podemos autowire sus dependencias. En este caso necesitaremos un MongoTemplate para construir nuestra query dinámica. Emplearemos la inyección basada en el constructor para inyectar una instancia de MonogTemplate.

Como puede observarse, esta clase implementa el método query y emplea el MongoTemplate inyectado para construir una consulta basada en Criteria.

La implementación del método comienza por creat una lista vacía de Criterias y comprueba si alguno de los campos proporcionados por DynamicQuery se han definido. Para cada uno de los campos definidos añade una nueva condición Criteria where. A continuación se fusionan todos los Critarias en la lista empleando un andOperator y añadiéndolos a la Query. Por último el método ejecuta la consulta en MongoTemplate para la colección especificada en la clase Book.

En este método también se puede observar como emplear una query con filtrado por expresiones regulares para consultar un campo (subjects) ignorando mayúsculas y minúsculas, o si un array (authorNames) contiene determinado texto.

Modificar el repositorio inicial para que extienda la interfaz a medida

El último paso será modificar el repositorio que hemos definido inicialmente en la interfaz BookRepository para que también extienda la interfaz a medida:

Probando el repositorio a medida

La prueba unitaria de repositorios empleando una base de datos MongoDB embebida se mostrará en otro tutorial.

Vamos a crear una clase BookRepositoryTest que probará el método auto-implementado por Spring y nuestro método a medida query.

El primer test del fragmento mostrado (findByTitleContainingOrderByTitle_existingTitle_shouldReturnList) comprueba que el método auto-implementado findByTitleContainingOrderByTitle funciona como se espera, devolviendo una lista de libros cuyo título contenga una parte del texto proporcionado teniendo en cuenta mayúsculas y minúsculas.

El segundo test del fragmento (query_combinedQuery_shouldReturnList) comprueba que nuestra implementación a medida del método query funciona para un DynamicQuery con varios campos definidos.

Desde una perspectiva a más bajo nivel, podemos ver que el objeto representando la implementación de la interfaz BookRepository es sencillamente un Proxy:

BookRepository is Proxy

Dependiendo del método que invoquemos, el proxy llamará a una implementación u otra (i.e. SimpleMongoRepository or BookRepositoryImpl).

Conclusión

En esta publicación hemos visto como añadir métodos a medida para un repositorio MongoDB de Spring-Data y como implementarlos.

Una de las principales cuestiones a tener en cuenta, si no se va a proporcionar una configuración específica, es la de respetar las convenciones de nombres para las interfaces a medida y sus respectivas implementaciones.

El código fuente completo de este artículo puede encontrarse en GitHub.

Spring-Data + MongoDB

Tweet about this on TwitterShare on FacebookShare on LinkedInShare on Google+Pin on PinterestEmail this to someone

Dejar un Comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *