Modelar un Negocio en Spring Boot y Hibernate — Parte II

La capa de acceso a datos

Alan Badillo Salas
Full Stack Java
4 min readAug 28, 2018

--

En nuestra entrega anterior revisamos la Capa de datos, la cual consiste en definir las entidades de nuestro negocio y su relación a la base de datos mediante Hibernate. Ahora es momento de revisar la segunda capa llamada la Capa de Acceso a Datos o DAO (Data Access Object).

Esta capa se enfoca en brindar el acceso a los datos almacenados en la base de datos y entregarlos como las clases POJO definidas en la capa de datos. El mecanismo para realizar esto será crear una interfaz derivada de CrudRepository la cual proveerá métodos ya autoprogramados para realizar consultas a la base de datos y la cuál podremos extender con nuestras propias consultas personalizadas.

La capa DAO será en sí una instancia creada y administrada por Hibernate, con la cuál podremos consumir la base de datos mediante el concepto de Repositorio.

Definir la interfaz del repositorio

Un repositorio es un sistema que administra el almacenamiento y recuperación de datos en un sistema. Para reducir la complejidad es bueno utilizar un repositorio por cada Entidad definida en nuestro sistema. Así por ejemplo si tenemos la entidad Cliente, deberemos definir su repositorio ClienteRepository o ClienteDAO.

Lo primero será crear el paquete com.miempresa.dao donde almacenaremos todas las interfaces (repositorios). Dentro de este paquete crearemos interfaces como en la capa de datos DTO creabamos clases definiendo las entidades. Para que la interfaz funcione como un repositorio debemos marcar la interfaz con la anotación Transactional de javax.transaction.* y extender la interfaz genérica CrudRepository<T, S> donde T es la clase POJO del DTO de la entidad a trabajar y S es el tipo de dato para el campo marcado como ID que generalmente es Integer o Long.

Observa que la interfaz llamada CupcakeRepository ha sido marcada con la anotación Transactional y que extiende (deriva) de CrudRepository<Cupcake, Integer>, esto significa que nuestro repositorio tiene acceso a la entidad Cupcake y que además puede ya realizar búsquedas genéricas en la base de datos tales como findAll() => Iterable<Cupcake>, findById(Integer id) => Optional<Cupcake>, save(Cupcake cupcake), etc. Los métodos genéricos que provee por defecto un CrudRepository se generan automáticamente ya que se sabe a que tabla será mapeada la entidad, cuál es el tipo de dato de su ID y qué campos contiene.

Definir un Query personalizado en el Repositorio

Muchas veces no bastarán los métodos genéricos provistos como findAll o findById, por lo que podemos definir nuestras propias consultas SQL fácilmente.

Existe una convensión que dice que si llamamos a nuestra consulta findByX entonces se creará automáticamente el query que haga la busqueda para el campo X y sólo deberemos definir el método de consumo.

En el ejemplo anterior podemos observar que se define el método findByNombre que recibe String nombre. De está forma Hibernate puede intuir bajo la convención findBy… que nos referimos al query select * from cupcakes where nombre=:nombre enviando el parámetro nombre como reemplazo para :nombre. La consulta anterior nos devolverá una entidad de tipo Cupcake ya que eso nos devuelve la base de datos, así que podemos devolver Optional<Cupcake> o simplemente Cupcake, la diferencia es que Optional<Cupcake> nos devuelve un objeto al que podremos preguntarle si existe y Cupcake nos devolvería null en caso de que no se devuelvan resultados.

Si queremos realizar una consulta que no cumpla la convesión anterior, por ejemplo, un método llamado costoMayorA(Float costo) que devuelva una lista iterable de Cupcakes cuyo campo costo sea mayor a pasado como parámetro, entonces deberemos establecer el query select * from cupcakes where costo > :costo, en temas siguientes profundizaremos más este tema para lograr realizar queries de tipo join y más complicados.

Entonces definimos el query mediante la anotación Query y en el método solicitamos los parámetros de reemplazo para el query como se muestra a continuación.

Observa que en la anotación Query definimos el parámetro value con la consulta específica y el parámetro nativeQuery=true para especificar que es un query nativo del dialecto (en este caso MySQL). También hay que notar que dentro del query tenemos :costo el cuál será reemplazado con el indicado en costoMayorA(Float costo).

Conclusiones

La capa de Acceso a Datos mejor conocida como DAO, nos permite definir métodos para consumir nuestra base de datos de forma fácil, no sólo podemos definir consultas de tipo SELECT, también podemos definir queries de tipo INSERT, UPDATE, DELETE, etc. En entregas futuras profundizaremos otra vez sobre esta capa para realizar consultas avanzadas.

La capa DAO consiste en interfaces marcadas con la anotación Transactional y derivan de CrudRepository<T, S> con la cual Hibernate puede implementar toda la funcionalidad correspondiente para que las consultas definidas en cada método funcionen. Esto nos ahorra bastante tiempo al momento de trabajar las consultas a nuestra base de datos y hace que tengamos un sistema que no se pierde en código (anteriormente deberíamos definir la conexión a la base de datos, crear statements, preparar los statements y ejecutar los statements).

La capa DAO debe ser consumida singularmente por los servicios para garantizar la seguridad de nuestro sistema, no deberíamos permitir que un controlador REST consuma directamente nuestra capa DAO al menos que los datos no sean sensibles y no requieran seguridad.

Envía tus comentarios sobre las dudas que tengas sobre esta capa para ampliar está entrega y no olvides visitar la siguiente capa llamada la Capa de Servicios. ¡Hasta la próxima!

--

--

Alan Badillo Salas
Full Stack Java

L. Matemáticas Aplicadas UAM-Cuajimalpa. M. en Inteligencia Artificial IPN-CIDETEC. Desarrollador Full Stack MEAN/MERN. Data Scientist.