SpringBoot
Tutorial SpringBoot: Spring Data R2DBC
Accede de forma reactiva a MySQL, H2 y bases de datos relacionales con Spring Data R2DBC en aplicaciones Spring Boot reactivas y no bloqueantes para máximo rendimiento.
Aprende SpringBoot GRATIS y certifícateStarter R2DBC y drivers reactivo para MySQL y H2
Para trabajar con bases de datos de forma reactiva en Spring Boot, es necesario utilizar R2DBC (Reactive Relational Database Connectivity). Este enfoque permite interactuar con bases de datos relacionales de manera no bloqueante, aprovechando al máximo los recursos del sistema.
Para comenzar, debemos agregar las dependencias necesarias en nuestro archivo de compilación. Si utilizamos Maven, incluiremos el starter de Spring Data R2DBC y los drivers reactivos correspondientes:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.asyncer</groupId>
<artifactId>r2dbc-mysql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
En el caso de utilizar Gradle, las dependencias se añadirían de la siguiente manera:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-r2dbc'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'com.mysql:mysql-connector-j'
runtimeOnly 'io.asyncer:r2dbc-mysql'
runtimeOnly 'io.r2dbc:r2dbc-h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.projectreactor:reactor-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
Es importante asegurarse de utilizar las versiones más recientes de los drivers para garantizar la compatibilidad y aprovechar las últimas mejoras.
Una vez añadidas las dependencias, configuraremos la conexión a la base de datos en el archivo application.properties
o application.yml
. Por ejemplo, para conectar con una base de datos MySQL:
spring.r2dbc.url=r2dbc:mysql://localhost:3306/mi_base_datos
spring.r2dbc.username=usuario
spring.r2dbc.password=contraseña
Si deseamos utilizar una base de datos H2 en memoria para propósitos de desarrollo o pruebas, la configuración sería:
spring.r2dbc.url=r2dbc:h2:mem:///testdb
spring.r2dbc.username=sa
spring.r2dbc.password=
Al utilizar H2, es posible inicializar la base de datos con scripts SQL. Para ello, añadimos:
spring.r2dbc.initialization-mode=always
spring.r2dbc.schema=classpath:schema.sql
spring.r2dbc.data=classpath:data.sql
Es relevante destacar que, a diferencia de JDBC, R2DBC utiliza un protocolo asíncrono y no bloqueante, lo que mejora el rendimiento en aplicaciones que manejan un gran volumen de solicitudes simultáneas.
Opcionalmente, para garantizar que nuestra aplicación utilice el pool de conexiones adecuado, podemos configurar un proveedor como ConnectionFactoryOptions. Un ejemplo de configuración para MySQL sería:
import io.r2dbc.spi.ConnectionFactory;
import io.r2dbc.spi.ConnectionFactoryOptions;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DatabaseConfig {
@Bean
public ConnectionFactory connectionFactory() {
return ConnectionFactoryOptions.builder()
.option(DRIVER, "mysql")
.option(HOST, "localhost")
.option(PORT, 3306)
.option(USER, "usuario")
.option(PASSWORD, "contraseña")
.option(DATABASE, "mi_base_datos")
.build();
}
}
Esta configuración nos permite tener un control más fino sobre las opciones de conexión. Además, al definir el ConnectionFactory como un bean, Spring Boot lo utilizará automáticamente en nuestros repositorios reactivos.
Es aconsejable utilizar propiedades específicas para el driver que estemos empleando. Por ejemplo, para MySQL podemos optimizar el rendimiento ajustando ciertas opciones en el ConnectionFactoryOptions
.
Finalmente, es esencial comprender que el uso de R2DBC implica trabajar con tipos Publisher como Flux
y Mono
de Reactor, lo cual es fundamental para el manejo reactivo de datos en nuestra aplicación.
Crear entidad JPA y repositorio extendiendo R2DBCRepository
Para comenzar a interactuar con una base de datos de forma reactiva utilizando Spring Data R2DBC, es necesario definir nuestras entidades y los repositorios correspondientes. Aunque trabajamos en un entorno reactivo, la definición de las entidades es similar a como se haría con JPA, aplicando ciertas consideraciones específicas para R2DBC.
El primer paso es crear una clase que represente nuestra entidad. Supongamos que estamos trabajando con una entidad llamada Producto
. La clase se definiría de la siguiente manera:
package com.ejemplo.tienda.entidad;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;
@Table("productos")
public class Producto {
@Id
private Long id;
private String nombre;
private Double precio;
// Constructores, getters y setters
public Producto() {
}
// Getters y setters omitidos para mayor claridad
}
Es importante destacar que utilizamos la anotación @Table
de Spring Data R2DBC para especificar el nombre de la tabla en la base de datos. Además, la anotación @Id
indica el campo que actúa como clave primaria. A diferencia de JPA, no utilizamos @Entity
ni otras anotaciones de JPA, ya que R2DBC no es compatible con el estándar JPA.
En cuanto a los tipos de datos, es recomendable utilizar tipos de datos envoltorios (como Long
, Integer
, Double
) en lugar de tipos primitivos, para manejar adecuadamente valores nulos y evitar posibles errores de mapeo.
Una vez definida la entidad, procedemos a crear el repositorio correspondiente. Este repositorio nos permitirá realizar operaciones CRUD de forma reactiva sobre la entidad Producto
. El repositorio se define como una interfaz que extiende de R2dbcRepository
:
package com.ejemplo.tienda.repositorio;
import com.ejemplo.tienda.entidad.Producto;
import org.springframework.data.r2dbc.repository.R2dbcRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductoRepositorio extends R2dbcRepository<Producto, Long> {
// Métodos personalizados si es necesario
}
Al extender de R2dbcRepository<Producto, Long>
, especificamos que este repositorio manejará entidades de tipo Producto
con claves primarias de tipo Long
. La interfaz R2dbcRepository
proporciona métodos reactivos básicos como save()
, findById()
, findAll()
, deleteById()
, entre otros, que devuelven tipos Mono
o Flux
según corresponda.
Es conveniente anotar el repositorio con @Repository
para que Spring lo detecte y lo gestione como un componente dentro del contexto de la aplicación.
Si necesitamos definir métodos personalizados, podemos hacerlo siguiendo las convenciones de nomenclatura de Spring Data. Por ejemplo, para encontrar productos por nombre:
import reactor.core.publisher.Flux;
Flux<Producto> findByNombre(String nombre);
Este método devolverá un Flux
de Producto
, permitiendo manejar el flujo de datos de manera asíncrona y no bloqueante.
Además, si requerimos ejecutar consultas más complejas, podemos utilizar la anotación @Query
específica para R2DBC:
import org.springframework.data.r2dbc.repository.Query;
import reactor.core.publisher.Flux;
@Query("SELECT * FROM productos WHERE precio < :precio")
Flux<Producto> encontrarProductosBaratos(Double precio);
Es fundamental asegurarse de que las consultas y las operaciones que definamos sean compatibles con el paradigma reactivo, aprovechando las capacidades que ofrece R2DBC.
Al integrar estos componentes en nuestra aplicación, podremos inyectar el repositorio ProductoRepositorio
en nuestros servicios o controladores y realizar operaciones de acceso a datos de forma eficiente y escalable.
Por ejemplo, en un servicio podríamos tener:
package com.ejemplo.tienda.servicio;
import com.ejemplo.tienda.entidad.Producto;
import com.ejemplo.tienda.repositorio.ProductoRepositorio;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Service
public class ProductoServicio {
private final ProductoRepositorio productoRepositorio;
public ProductoServicio(ProductoRepositorio productoRepositorio) {
this.productoRepositorio = productoRepositorio;
}
public Flux<Producto> obtenerTodos() {
return productoRepositorio.findAll();
}
public Mono<Producto> obtenerPorId(Long id) {
return productoRepositorio.findById(id);
}
public Mono<Producto> guardar(Producto producto) {
return productoRepositorio.save(producto);
}
public Mono<Void> eliminar(Long id) {
return productoRepositorio.deleteById(id);
}
}
En este servicio, cada método devuelve un Mono
o un Flux
, permitiendo que las llamadas sean no bloqueantes y se integren fácilmente con el resto de la aplicación reactiva.
Al implementar esta estructura, nos aseguramos de que nuestra aplicación esté preparada para manejar múltiples solicitudes de forma concurrente, optimizando el uso de los recursos e incrementando el rendimiento.
Finalmente, es recomendable revisar la documentación oficial de Spring Data R2DBC para profundizar en las capacidades avanzadas y estar al tanto de las mejores prácticas en el desarrollo de aplicaciones reactivas con Spring Boot 3.
Creación de métodos en repositorios con respuestas reactivas
En aplicaciones reactivas con Spring Data R2DBC, es común necesitar consultas personalizadas que vayan más allá de los métodos básicos proporcionados por R2dbcRepository
. Para ello, podemos crear métodos en nuestros repositorios que devuelvan respuestas reactivas utilizando Flux
y Mono
.
Una forma de obtener consultas personalizadas es mediante la derivación de consultas a partir del nombre del método. Por ejemplo, si deseamos encontrar productos por rango de precio, podemos definir en nuestro repositorio:
import reactor.core.publisher.Flux;
Flux<Producto> findByPrecioBetween(Double min, Double max);
Este método utilizará las convenciones de nomenclatura de Spring Data para generar automáticamente la consulta SQL correspondiente. La respuesta será un Flux<Producto>
, lo que nos permite manejar un flujo de múltiples productos de forma asíncrona.
Si necesitamos consultas más complejas que no pueden expresarse mediante la derivación de nombres, podemos utilizar la anotación @Query
de Spring Data R2DBC. Esto nos permite escribir consultas SQL nativas y asociarlas a métodos en el repositorio. Por ejemplo:
import org.springframework.data.r2dbc.repository.Query;
import reactor.core.publisher.Flux;
@Query("SELECT * FROM productos WHERE LOWER(nombre) LIKE LOWER(CONCAT('%', :nombre, '%'))")
Flux<Producto> buscarPorNombreConteniendo(String nombre);
En este caso, el método buscarPorNombreConteniendo
devolverá todos los productos cuyo nombre contenga la cadena proporcionada, sin importar mayúsculas o minúsculas. La consulta utiliza parámetros nombrados (:nombre
), lo que mejora la legibilidad y evita problemas de inyección SQL.
Es importante destacar que, al utilizar @Query
, debemos asegurarnos de que la sintaxis SQL sea compatible con la base de datos que estemos utilizando. Además, debemos manejar adecuadamente los tipos de retorno para que sean reactivos.
Para operaciones que devuelven un único resultado, usamos Mono
. Por ejemplo, si queremos encontrar el producto con el precio más alto:
import reactor.core.publisher.Mono;
@Query("SELECT * FROM productos ORDER BY precio DESC LIMIT 1")
Mono<Producto> encontrarProductoMasCaro();
Este método nos proporciona un Mono<Producto>
, representando la posibilidad de que exista o no un resultado. El uso de Mono
es apropiado cuando esperamos cero o un elemento como respuesta.
Cuando trabajamos con funciones agregadas o necesitamos devolver tipos primitivos, también podemos utilizar Mono
para encapsular el resultado. Por ejemplo, para obtener el número total de productos:
import reactor.core.publisher.Mono;
@Query("SELECT COUNT(*) FROM productos")
Mono<Long> contarProductos();
Aquí, contarProductos
devolverá un Mono<Long>
con el total de registros en la tabla productos
.
La paginación y el ordenamiento son aspectos clave en aplicaciones que manejan grandes volúmenes de datos. Aunque R2DBC aún no soporta nativamente la interfaz Pageable
de Spring Data, podemos implementar paginación manualmente en nuestras consultas:
@Query("SELECT * FROM productos ORDER BY precio ASC LIMIT :limit OFFSET :offset")
Flux<Producto> encontrarProductosPaginados(int limit, int offset);
En este ejemplo, podemos obtener un subconjunto de productos ordenados por precio de manera ascendente. Los parámetros :limit
y :offset
controlan el tamaño de la página y el desplazamiento, respectivamente.
Para mejorar la reutilización de código y mantener un diseño modular, es posible combinar métodos predefinidos con consultas personalizadas. Por ejemplo, si queremos reutilizar criterios comunes:
Flux<Producto> findByCategoriaAndDisponible(String categoria, Boolean disponible);
Este método nos permite obtener los productos que pertenecen a una determinada categoría y que están disponibles, aprovechando la derivación de consultas por nombre.
Al manejar entidades relacionadas, podemos definir métodos que trabajen con las relaciones establecidas. Si un producto tiene una lista de reseñas, podríamos obtenerlas así:
import reactor.core.publisher.Flux;
Flux<Reseña> findReseñasByProductoId(Long productoId);
Es fundamental que nuestras entidades estén correctamente mapeadas y que las relaciones sean compatibles con R2DBC, ya que este no soporta todas las características de JPA. En este contexto, debemos manejar las relaciones de manera explícita y considerar cargar los datos de forma manual si es necesario.
Al crear métodos en repositorios con respuestas reactivas, debemos prestar atención a la gestión de errores. Los tipos Flux
y Mono
nos permiten propagar excepciones y manejarlas en capas superiores. Por ejemplo, podemos utilizar operadores como onErrorResume
para proporcionar valores por defecto o alternativas en caso de fallos:
Mono<Producto> obtenerProductoSeguro(Long id) {
return productoRepositorio.findById(id)
.switchIfEmpty(Mono.error(new ProductoNoEncontradoException("Producto no encontrado")))
.onErrorResume(e -> Mono.empty());
}
Este enfoque nos da flexibilidad para controlar el flujo de datos y las posibles excepciones de forma reactiva y elegante.
La creación de métodos en repositorios con respuestas reactivas en Spring Data R2DBC nos permite construir aplicaciones eficientes y escalables. Al aprovechar las capacidades de derivación de consultas, el uso de @Query
y el manejo de Flux
y Mono
, podemos adaptar nuestros repositorios a las necesidades específicas de nuestra aplicación, manteniendo un código limpio y conciso.
Ejercicios de esta lección Spring Data R2DBC
Evalúa tus conocimientos de esta lección Spring Data R2DBC con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
API Query By Example (QBE)
Identificadores y relaciones JPA
Borrar datos de base de datos
Web y Test Starters
Métodos find en repositorios
Controladores Spring MVC
Inserción de datos
CRUD Customers Spring MVC + Spring Data JPA
Backend API REST con Spring Boot
Controladores Spring REST
Uso de Spring con Thymeleaf
API Specification
Registro de usuarios
Crear entidades JPA
Asociaciones en JPA
Asociaciones de entidades JPA
Integración con Vue
Consultas JPQL
Open API y cómo agregarlo en Spring Boot
Uso de Controladores REST
Repositorios reactivos
Inyección de dependencias
Introducción a Spring Boot
CRUD y JPA Repository
Inyección de dependencias
Vista en Spring MVC con Thymeleaf
Servicios en Spring
Operadores Reactivos
Configuración de Vue
Entidades JPA
Integración con Angular
API Specification
API Query By Example (QBE)
Controladores MVC
Anotaciones y mapeo en JPA
Consultas JPQL con @Query en Spring Data JPA
Repositorios Spring Data
Inyección de dependencias
Data JPA y Mail Starters
Configuración de Angular
Controladores Spring REST
Configuración de Controladores MVC
Consultas JPQL con @Query en Spring Data JPA
Actualizar datos de base de datos
Verificar token JWT en peticiones
Login de usuarios
Integración con React
Configuración de React
Todas las lecciones de SpringBoot
Accede a todas las lecciones de SpringBoot y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Spring Boot
Introducción Y Entorno
Spring Boot Starters
Introducción Y Entorno
Inyección De Dependencias
Introducción Y Entorno
Controladores Spring Mvc
Spring Web
Vista En Spring Mvc Con Thymeleaf
Spring Web
Controladores Spring Rest
Spring Web
Open Api Y Cómo Agregarlo En Spring Boot
Spring Web
Servicios En Spring
Spring Web
Clientes Resttemplate Y Restclient
Spring Web
Rxjava En Spring Web
Spring Web
Crear Entidades Jpa
Persistencia Spring Data
Asociaciones De Entidades Jpa
Persistencia Spring Data
Repositorios Spring Data
Persistencia Spring Data
Métodos Find En Repositorios
Persistencia Spring Data
Inserción De Datos
Persistencia Spring Data
Actualizar Datos De Base De Datos
Persistencia Spring Data
Borrar Datos De Base De Datos
Persistencia Spring Data
Consultas Jpql Con @Query En Spring Data Jpa
Persistencia Spring Data
Api Query By Example (Qbe)
Persistencia Spring Data
Api Specification
Persistencia Spring Data
Repositorios Reactivos
Persistencia Spring Data
Introducción E Instalación De Apache Kafka
Mensajería Asíncrona
Crear Proyecto Con Apache Kafka
Mensajería Asíncrona
Creación De Producers
Mensajería Asíncrona
Creación De Consumers
Mensajería Asíncrona
Kafka Streams En Spring Boot
Mensajería Asíncrona
Introducción A Spring Webflux
Reactividad Webflux
Spring Data R2dbc
Reactividad Webflux
Controlador Rest Reactivo Basado En Anotaciones
Reactividad Webflux
Controlador Rest Reactivo Funcional
Reactividad Webflux
Operadores Reactivos Básicos
Reactividad Webflux
Operadores Reactivos Avanzados
Reactividad Webflux
Cliente Reactivo Webclient
Reactividad Webflux
Introducción A Spring Security
Seguridad Con Spring Security
Seguridad Basada En Formulario En Mvc Con Thymeleaf
Seguridad Con Spring Security
Registro De Usuarios
Seguridad Con Spring Security
Login De Usuarios
Seguridad Con Spring Security
Verificar Token Jwt En Peticiones
Seguridad Con Spring Security
Seguridad Jwt En Api Rest Spring Web
Seguridad Con Spring Security
Seguridad Jwt En Api Rest Reactiva Spring Webflux
Seguridad Con Spring Security
Autenticación Y Autorización Con Anotaciones
Seguridad Con Spring Security
Testing Unitario De Componentes Y Servicios
Testing Con Spring Test
Testing De Repositorios Spring Data Jpa Y Acceso A Datos Con Spring Test
Testing Con Spring Test
Testing Controladores Spring Mvc Con Thymeleaf
Testing Con Spring Test
Testing Controladores Rest Con Json
Testing Con Spring Test
Testing De Aplicaciones Reactivas Webflux
Testing Con Spring Test
Testing De Seguridad Spring Security
Testing Con Spring Test
Testing Con Apache Kafka
Testing Con Spring Test
Integración Con Angular
Integración Frontend
Integración Con React
Integración Frontend
Integración Con Vue
Integración Frontend
En esta lección
Objetivos de aprendizaje de esta lección
- Conexión reactiva con base de datos
- Conocer R2DBC
- Respuestas Flux y Mono en repositorios