Spring Boot

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ícate

Starter 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.

Aprende SpringBoot GRATIS online

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)

Spring Boot
Test

Identificadores y relaciones JPA

Spring Boot
Puzzle

Borrar datos de base de datos

Spring Boot
Test

Web y Test Starters

Spring Boot
Puzzle

Métodos find en repositorios

Spring Boot
Test

Controladores Spring MVC

Spring Boot
Código

Inserción de datos

Spring Boot
Test

CRUD Customers Spring MVC + Spring Data JPA

Spring Boot
Proyecto

Backend API REST con Spring Boot

Spring Boot
Proyecto

Controladores Spring REST

Spring Boot
Código

Uso de Spring con Thymeleaf

Spring Boot
Puzzle

API Specification

Spring Boot
Puzzle

Registro de usuarios

Spring Boot
Test

Crear entidades JPA

Spring Boot
Código

Asociaciones en JPA

Spring Boot
Test

Asociaciones de entidades JPA

Spring Boot
Código

Integración con Vue

Spring Boot
Test

Consultas JPQL

Spring Boot
Código

Open API y cómo agregarlo en Spring Boot

Spring Boot
Puzzle

Uso de Controladores REST

Spring Boot
Puzzle

Repositorios reactivos

Spring Boot
Test

Inyección de dependencias

Spring Boot
Test

Introducción a Spring Boot

Spring Boot
Test

CRUD y JPA Repository

Spring Boot
Puzzle

Inyección de dependencias

Spring Boot
Código

Vista en Spring MVC con Thymeleaf

Spring Boot
Test

Servicios en Spring

Spring Boot
Código

Operadores Reactivos

Spring Boot
Puzzle

Configuración de Vue

Spring Boot
Puzzle

Entidades JPA

Spring Boot
Test

Integración con Angular

Spring Boot
Test

API Specification

Spring Boot
Test

API Query By Example (QBE)

Spring Boot
Puzzle

Controladores MVC

Spring Boot
Test

Anotaciones y mapeo en JPA

Spring Boot
Puzzle

Consultas JPQL con @Query en Spring Data JPA

Spring Boot
Test

Repositorios Spring Data

Spring Boot
Test

Inyección de dependencias

Spring Boot
Puzzle

Data JPA y Mail Starters

Spring Boot
Test

Configuración de Angular

Spring Boot
Puzzle

Controladores Spring REST

Spring Boot
Test

Configuración de Controladores MVC

Spring Boot
Puzzle

Consultas JPQL con @Query en Spring Data JPA

Spring Boot
Puzzle

Actualizar datos de base de datos

Spring Boot
Test

Verificar token JWT en peticiones

Spring Boot
Test

Login de usuarios

Spring Boot
Test

Integración con React

Spring Boot
Test

Configuración de React

Spring Boot
Puzzle

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

Spring Boot

Introducción Y Entorno

Spring Boot Starters

Spring Boot

Introducción Y Entorno

Inyección De Dependencias

Spring Boot

Introducción Y Entorno

Controladores Spring Mvc

Spring Boot

Spring Web

Vista En Spring Mvc Con Thymeleaf

Spring Boot

Spring Web

Controladores Spring Rest

Spring Boot

Spring Web

Open Api Y Cómo Agregarlo En Spring Boot

Spring Boot

Spring Web

Servicios En Spring

Spring Boot

Spring Web

Clientes Resttemplate Y Restclient

Spring Boot

Spring Web

Rxjava En Spring Web

Spring Boot

Spring Web

Crear Entidades Jpa

Spring Boot

Persistencia Spring Data

Asociaciones De Entidades Jpa

Spring Boot

Persistencia Spring Data

Repositorios Spring Data

Spring Boot

Persistencia Spring Data

Métodos Find En Repositorios

Spring Boot

Persistencia Spring Data

Inserción De Datos

Spring Boot

Persistencia Spring Data

Actualizar Datos De Base De Datos

Spring Boot

Persistencia Spring Data

Borrar Datos De Base De Datos

Spring Boot

Persistencia Spring Data

Consultas Jpql Con @Query En Spring Data Jpa

Spring Boot

Persistencia Spring Data

Api Query By Example (Qbe)

Spring Boot

Persistencia Spring Data

Api Specification

Spring Boot

Persistencia Spring Data

Repositorios Reactivos

Spring Boot

Persistencia Spring Data

Introducción E Instalación De Apache Kafka

Spring Boot

Mensajería Asíncrona

Crear Proyecto Con Apache Kafka

Spring Boot

Mensajería Asíncrona

Creación De Producers

Spring Boot

Mensajería Asíncrona

Creación De Consumers

Spring Boot

Mensajería Asíncrona

Kafka Streams En Spring Boot

Spring Boot

Mensajería Asíncrona

Introducción A Spring Webflux

Spring Boot

Reactividad Webflux

Spring Data R2dbc

Spring Boot

Reactividad Webflux

Controlador Rest Reactivo Basado En Anotaciones

Spring Boot

Reactividad Webflux

Controlador Rest Reactivo Funcional

Spring Boot

Reactividad Webflux

Operadores Reactivos Básicos

Spring Boot

Reactividad Webflux

Operadores Reactivos Avanzados

Spring Boot

Reactividad Webflux

Cliente Reactivo Webclient

Spring Boot

Reactividad Webflux

Introducción A Spring Security

Spring Boot

Seguridad Con Spring Security

Seguridad Basada En Formulario En Mvc Con Thymeleaf

Spring Boot

Seguridad Con Spring Security

Registro De Usuarios

Spring Boot

Seguridad Con Spring Security

Login De Usuarios

Spring Boot

Seguridad Con Spring Security

Verificar Token Jwt En Peticiones

Spring Boot

Seguridad Con Spring Security

Seguridad Jwt En Api Rest Spring Web

Spring Boot

Seguridad Con Spring Security

Seguridad Jwt En Api Rest Reactiva Spring Webflux

Spring Boot

Seguridad Con Spring Security

Autenticación Y Autorización Con Anotaciones

Spring Boot

Seguridad Con Spring Security

Testing Unitario De Componentes Y Servicios

Spring Boot

Testing Con Spring Test

Testing De Repositorios Spring Data Jpa

Spring Boot

Testing Con Spring Test

Testing Controladores Spring Mvc Con Thymeleaf

Spring Boot

Testing Con Spring Test

Testing Controladores Rest Con Json

Spring Boot

Testing Con Spring Test

Testing De Aplicaciones Reactivas Webflux

Spring Boot

Testing Con Spring Test

Testing De Seguridad Spring Security

Spring Boot

Testing Con Spring Test

Testing Con Apache Kafka

Spring Boot

Testing Con Spring Test

Integración Con Angular

Spring Boot

Integración Frontend

Integración Con React

Spring Boot

Integración Frontend

Integración Con Vue

Spring Boot

Integración Frontend

Accede GRATIS a SpringBoot y certifícate

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