Unblock Your Applications with R2DBC, Spring Data and MariaDB

spacer

Today, we’re excited to announce our new MariaDB Reactive Relational Database Connectivity (R2DBC) connector is GA. The connector implements the most recent R2DBC 0.8.3 specification which provides a non-blocking, fully-reactive API used to communicate with relational sources.

The JDBC API, made available through MariaDB Connector/J, has long been a popular choice among Java developers for creating applications that connect to and communicate with MariaDB. However, with the introduction of MariaDB’s new R2DBC driver, developers now have the ability to interact with MariaDB databases completely non-blocked through the use of asynchronous data streams.

While it’s certainly possible to use MariaDB Connector/R2DBC directly within your applications, there are tools and frameworks that eliminate much of the pain involved in creating resilient data access layers.

In this article, we’ll walk through the steps for setting up a Maven-based Java application to manage TODO tasks. Though simple, the application will demonstrate the fundamentals of configuring and using MariaDB Connector/R2DBC. It will also use the Spring framework to show how quick and easy it is to get started creating fully-reactive applications with MariaDB.

What is R2DBC?

Before we dive into the steps, let’s do a quick refresher on R2DBC. In 2018, R2DBC started as an experiment and proof of concept to enable integration of SQL databases into systems that use reactive programming models to overcome the limitations of the Java Database Connectivity (JDBC) API. Still the new kid on the block, R2DBC has quickly become the default specification for reactive programming with relational databases, and is slated for General Availability (GA) in 2021!

More specifically, R2DBC specifies a service-provider interface (SPI) that is intended to be implemented by driver vendors, like MariaDB, and used by client libraries, like Spring Data. Using the R2DBC SPI, which is built on top of the Reactive Streams API, the new MariaDB Connector/R2DBC enables applications written in a JVM programming language to access MariaDB using reactive programming models. All communication is event-driven, non-blocking, and makes no assumptions about concurrency or asynchronicity.

If you’d like to learn more about the MariaDB Connector for R2DBC, join our webinar or visit our new MariaDB Developer Hub for additional resources and code samples. Keep reading for a step-by-step guide to create a fully-reactive application with MariaDB using the new MariaDB Connector/R2DBC.

Requirements

Before jumping into code, you’re going to need to make sure you have a few things on your machine:

Get Started with MariaDB

However, before you can create a new Java application that utilizes the MariaDB Connector/R2DBC you will need to have access to a MariaDB Server instance. We recommend either of these two methods:

1. Download MariaDB Server on your own hardware. See our Deployment Guide for step-by-step instructions in our documentation.

OR

2. Use MariaDB SkySQL cloud database to deploy MariaDB Platform, which includes MariaDB Enterprise Server. See our documentation to launch a database service in minutes.

Prepare a MariaDB database

To test the MariaDB Connector/R2DBC, you’re going to need a MariaDB schema to interact with from the application you’ll be creating.

Schema creation

Start by using a client of your choice to connect to your MariaDB instance.

Next, create a new database, named «todo», for our application:

CREATE DATABASE todo;

Then create a new table to store tasks:

CREATE TABLE todo.tasks (id int AUTO_INCREMENT PRIMARY KEY, description 
varchar(200), completed boolean DEFAULT false);
Add a new user

Optionally, you can create a new user that MariaDB Connector/R2DBC can use to connect to and communicate with your MariaDB database instance.

CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'Password123!';
GRANT ALL PRIVILEGES ON *.* TO 'app_user'@'localhost' WITH GRANT OPTION;

Going forward, app_user will be the user referenced in this walkthrough.

Generate a Maven project

After setting up the database it’s time to create a new application. There are a variety of ways to create new Java projects. For the purposes of this walkthrough, you’ll be using a popular site, called Spring Initializr, to generate a new Java Spring Boot application to expose API endpoints to be used to communicate with MariaDB using R2DBC.

Start by opening a browser window and navigating to https://start.spring.io/.

The Spring Initializr website’s main page contains all the options we will need to generate a new Java Spring Boot application.

 

Select project settings

On the left side of the page, you will find a variety of options for configuring your new project. Feel free to leave all of the default values as they are, as this will be sufficient for the project you’ll be using going forward.

 

Add dependencies

On the right, you’ll notice a section called “Dependencies”. Here you’ll specify the Maven artifacts that you’ll be using within the project. Add the following dependencies using the ADD DEPENDENCIES button.

  • MariaDB Driver: The MariaDB Connector/R2DBC artifact.
  • Spring Data R2DBC: A Spring client library that abstracts away much of the data access scaffolding functionality needed to use R2DBC.
  • Spring Reactive Web: A reactive library for web (API) applications.
  • Lombok: A Java annotation library that helps reduce boilerplate code, like getters and setters for model objects.

Generate the project

Finally, click the “Generate” button at the bottom of the page to download a new fully generated, compressed Java Spring Boot application as demo.zip that includes the dependencies you just added.

Integrate R2DBC using Spring Data and MariaDB

Navigate to the location where you downloaded the compressed file and unzip to uncompress the Java project. Now you have a fully functional Apache Maven-based Java application, and it’s time to get a first-hand look at the MariaDB Connector/R2DBC and Spring Data R2DBC.

Note: There are a variety of ways to view and edit the contents of the Java project. For the purposes of this walkthrough, I’m going to make the assumption that you’re using an Integrated Development Environment (IDE) for navigating to and editing files. Ultimately, it makes no difference which IDE you use as I’ll be providing the Apache Maven terminal commands for building and running the applications towards the end of this article.

Add MariaDB connection settings

Before you can start writing code to take advantage of R2DBC within your application you’ll need to add connection information that Spring Data and the MariaDB R2DBC driver can use to connect to your MariaDB instance.

To do this navigate to /src/main/resources/application.properties, and add the following information:

  • spring.r2dbc.url: A valid R2DBC URL containing information about the R2DBC scheme, MariaDB driver, host and port authorities, and todo database. For more information please refer to the Official R2DBC specification documentation.
  • spring.r2dbc.username: The username for your MariaDB database instance.
  • spring.r2dbc.password: The password for your MariaDB database instance user (above).
spring.r2dbc.url=r2dbc:mariadb://127.0.0.1:3306/todo
spring.r2dbc.username=app_user
spring.r2dbc.password=Password123!
Mapping to the Tasks table

For the sake of simplicity, all of the remaining code will be added to a single file, DemoApplication.java.

Navigate to /src/main/java/com/example/demo/DemoApplication.java to get started.

Through a variety of annotation options, Spring Data provides the ability to map a Java class to a MariaDB database table.

Add the following import statements, and add the private class, named Task, to the DemoApplication.java file, outside of the scope of the DemoApplication class.

// The following import statements will be required within DemoApplication.java
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;
import org.springframework.lang.NonNull;
import lombok.Data;
 
// Task Model mapped to the tasks table
@Data
@RequiredArgsConstructor
@Table("tasks")
class Task {
   @Id
   private Integer id;
   @NonNull
   private String description;
   private Boolean completed;
}}
Repositories

Next, you can take advantage of a Spring Data R2DBC interface called ReactiveCrudRepository that provides generic CRUD operations on a repository for a specific type. This repository follows reactive paradigms and uses Project Reactor types which are built on top of Reactive Streams.

Create an implementation of ReactiveCrudRepository by pasting in the following code into the DemoApplication.java file, again outside of the scope of the DemoApplication class.

// The following import statements will be required within DemoApplication.java
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
 
// Tasks Repository 
interface TasksRepository extends ReactiveCrudRepository<Task, Integer> {  
}
Exposing with a Service and Controller

Having set up both a Task class and repository you now have the ability to read and write information to your MariaDB database using R2DBC!

Start by adding a Java Service implementation called TaskService, which contains Create-Read-Update-Delete (CRUD) functionality for the tasks table, as a private class to the DemoApplication.java file, outside of the scope of the DemoApplication class.

// The following import statements will be required within DemoApplication.java
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
 
// Tasks service class
 
@Service
class TaskService {
 
   @Autowired
   private TasksRepository repository;
   public Boolean isValid(final Task task) {
       if (task != null && !task.getDescription().isEmpty()) {
           return true;
       }
       return false;
   }
   public Flux getAllTasks() {
       return this.repository.findAll();
   }
   public Mono createTask(final Task task) {
       return this.repository.save(task);
   }
 
   @Transactional
   public Mono updateTask(final Task task) {
       return this.repository.findById(task.getId())
               .flatMap(t -> {
                   t.setDescription(task.getDescription());
                   t.setCompleted(task.getCompleted());
                   return this.repository.save(t);
               });
   }
 
   @Transactional
   public Mono deleteTask(final int id){
       return this.repository.findById(id)
               .flatMap(this.repository::delete);
   }
}

Then utilize the TaskService methods by adding a new Java Controller implementation called TasksController as a private class to the DemoApplication.java file, outside of the scope of the DemoApplication class.

// The following import statements will be required within DemoApplication.java
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
 
// Tasks controller class
@RestController
@RequestMapping("/tasks")
class TasksController {
 
   @Autowired
   private TaskService service;
 
   @GetMapping()
   public ResponseEntity<Flux> get() {
       return ResponseEntity.ok(this.service.getAllTasks());
   }
 
   @PostMapping()
   public ResponseEntity<Mono> post(@RequestBody Task task) {
       if (service.isValid(task)) {
           return ResponseEntity.ok(this.service.createTask(task));
       }
       return ResponseEntity.status(HttpStatus.I_AM_A_TEAPOT).build();
   }
 
   @PutMapping()
   public ResponseEntity<Mono> put(@RequestBody Task task) {
       if (service.isValid(task)) {
           return ResponseEntity.ok(this.service.updateTask(task));
       }
       return ResponseEntity.status(HttpStatus.I_AM_A_TEAPOT).build();
   }
 
   @DeleteMapping()
   public ResponseEntity<Mono> delete(@RequestParam int id) {
       if (id > 0) {
           return ResponseEntity.ok(this.service.deleteTask(id));
       }
       return ResponseEntity.status(HttpStatus.I_AM_A_TEAPOT).build();
   }
}

And voila, within this short time you’ve created a fully reactive Java Spring application using R2DBC!

R2DBC in action

To test the application, open a terminal window, navigate to the root folder of your Java project, and build the project using the following command:

$ mvn package

Then, if your application has been successfully built, run the application using the following command:

$ mvn spring-boot:run

Finally, use a few curl commands to test your application.

Add a new task

To add a new task execute the following command:

$ curl --header "Content-Type: application/json" \
--request POST \
--data "\"A New Task\"" \
http://localhost:8080/tasks
Retrieve all tasks

While you can certainly query the database directly to confirm that a new task record has been added, where’s the fun in that? Back to curl and the API!

$ curl http://localhost:8080/tasks

If all goes well you should receive the following JSON response:

[{ "id": 1, "description": "A New Task", "completed": false}]

To view this code in its entirety check out the source here.

And if you’re thinking «it’d sure be nice to see an implementation with a user interface», you’re in luck! You can find a fully fleshed out implementation of a TODO application using React.js and your choice of multiple API projects (R2DBC, JDBC, Node.js and Python) that integrate directly with MariaDB here!

Just the beginning

Hopefully, you enjoyed seeing how easy it is to get started using R2DBC with MariaDB and Spring Data. While this is a great introduction to reactive programming with MariaDB, we’ve only started to scratch the surface of what’s possible!

Join our upcoming R2DBC webinar.

Learn about developing modern applications with MariaDB on our new MariaDB Developer Hub.

Find the MariaDB Connector/R2DBC enterprise documentation.

Download the MariaDB Connector/R2DBC directly, on Maven or find it in the Spring Initializr.