All pages
Powered by GitBook
1 of 11

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Connect with MariaDB Connector/R2DBC (Spring Data)

Overview

Java developers can use MariaDB Connector/R2DBC to establish client connections with MariaDB database products.

Creating a Database Client

Connections are created, used, and managed using several classes:

Class
Description

Code Example: Connect

The following example shows how to use the DatabaseClient class to connect and execute queries:

After running the application App, verify that the table has been created:

Connection Pools

A DatabaseClient uses the underlying ConnectionFactory to get and release connections for each database operation without affinity to a particular connection across the multiple operations. When using Spring's R2DBC layer, a custom connection pool could be configured using an implementation provided by a third party.

This page is: Copyright © 2025 MariaDB. All rights reserved.

Using the Spring Data Framework with MariaDB Connector/R2DBC

Learn to integrate MariaDB Connector/R2DBC with Spring Data Framework. This guide covers reactive, non-blocking data access using Spring Data R2DBC for efficient and modern Java applications.

Overview

Java developers can use MariaDB Connector/R2DBC to connect to MariaDB database products using the Reactive Relational Database Connectivity (R2DBC) API. R2DBC operations are non-blocking, which makes it more scalable than Java's standard JDBC API. MariaDB Connector/R2DBC can be used with the very popular Spring Data framework, which can provide support for repositories, object mapping, and transaction management.

This page discusses how to use MariaDB Connector/R2DBC with the Spring Data framework.

For information on how to use MariaDB Connector/R2DBC with the native R2DBC API, refer to, .

Spring Data R2DBC

Spring Data R2DBC allows MariaDB Connector/R2DBC to be used with the popular Spring Data framework, which is part of the larger Spring Framework.

Spring Data R2DBC is currently in incubation, so it is not yet included with the main Spring Data modules.

Spring Data R2DBC supports many features from the Spring Data framework:

Spring Data Feature
Supported

DatabaseClient

Yes

Repositories

Yes

Object Mapping

Yes

Transaction Management

Yes

Resources

Using the native API of MariaDB Connector/R2DBC

org.mariadb.r2dbc.MariadbConnectionFactory

Creates client connections.

org.mariadb.r2dbc.MariadbConnectionConfiguration

Configures client connections for the connection factory.

io.r2dbc.spi.Connection

Implements the R2DBC client connection.

org.springframework.data.r2dbc.core.DatabaseClient

Creates a higher level, reactive client for Reactive Streams.

DDL with MariaDB Connector/R2DBC (Spring Data)

Overview

Java developers can use MariaDB Connector/R2DBC to connect to MariaDB database products using the Reactive Relational Database Connectivity (R2DBC) API. R2DBC operations are non-blocking, which makes it more scalable than Java's standard JDBC API. MariaDB Connector/R2DBC can be used with the very popular Spring Data framework, which can provide support for repositories, object mapping, and transaction management.

// Module Imports
import org.mariadb.r2dbc.MariadbConnectionConfiguration;
import org.mariadb.r2dbc.MariadbConnectionFactory;
import org.springframework.data.r2dbc.core.DatabaseClient;

import reactor.test.StepVerifier;

// Main Application Class
public class App {

   // Connection Configuration
   private static MariadbConnectionConfiguration conf;
   private static MariadbConnectionFactory connFactory;
   private static DatabaseClient client;

   // Main Process
   public static void main(String[] args) {

      try {
         // Configure the Connection
         conf = MariadbConnectionConfiguration.builder()
              .host("192.0.2.1").port(3306)
              .username("db_user").password("db_user_password")
              .database("test").build();

         // Instantiate a Connection Factory
         connFactory = new MariadbConnectionFactory(conf);

         // Instantiate a Database Client
         client = DatabaseClient.create(connFactory);

         // Create a Database Table

         client.execute("CREATE TABLE IF NOT EXISTS test.contact" + "(id INT PRIMARY KEY AUTO_INCREMENT,"
               + "first_name VARCHAR(25)," + "last_name VARCHAR(25)," + "email VARCHAR(25)) ENGINE=InnoDB")
               .fetch()
               .rowsUpdated()
               .as(StepVerifier::create)
               .expectNextCount(1)
               .verifyComplete();

      } catch (IllegalArgumentException e) {
         e.printStackTrace();
      } catch (io.r2dbc.spi.R2dbcNonTransientResourceException e) {
         e.printStackTrace();
      } finally {

      }
   }
}
SHOW TABLES;
+----------------+
| Tables_in_test |
+----------------+
| contact        |
+----------------+
DESCRIBE contact;
+------------+-------------+------+-----+--------------------------+
| Field      | Type        | Null | Key | Default  | Extra         |
+------------+-------------+------+-----+----------+---------------+
| id         | int(11)     | NO   | PRI | NULL     | auto_increment|
| first_Name | varchar(25) | YES  |     | NULL     |               |
| last_Name  | varchar(25) | YES  |     | NULL     |               |
| email      | varchar(25) | YES  |     | NULL     |               |
+------------+-------------+------+-----+----------+---------------+
DDL Operations

DDL (Data Definition Language) refers to all SQL-schema statements in the SQL standard (ISO/IEC 9075-2:2016).

Some examples of DDL include , , , , and .

With Spring Data, DDL operations can be performed by invoking the following methods:

Method
Description

DatabaseClient.execute(String sql)

Execute any DDL statement.

Code Example: CREATE TABLE

CREATE TABLE is a DDL (Data Definition Language) operation that creates a new table.

Complete the Setup for Examples and Create the Entity class before using the example.

The following example shows how to execute a CREATE TABLE statement:

After running the application App, verify the table has been created:

Code Example: TRUNCATE TABLE

The following code example truncates the test.contact table created above.

TRUNCATE is a DDL (Data Definition Language) operation that deletes all data from an existing table and resets the AUTO_INCREMENT column counter to 0:

  • A connection factory is used to create an instance of DatabaseClient to connect to the database.

  • The DROP and CREATE privileges are needed to truncate a table as TRUNCATE is a DDL statement that drops the table and creates a new table with the same table definition, in effect deleting all data.

This page is: Copyright © 2025 MariaDB. All rights reserved.

Batch Operations with MariaDB Connector/R2DBC (Spring Data)

Overview

Java developers can use MariaDB Connector/R2DBC to connect to MariaDB database products using the Reactive Relational Database Connectivity (R2DBC) API. R2DBC operations are non-blocking, which makes it more scalable than Java's standard JDBC API. MariaDB Connector/R2DBC can be used with the very popular Spring Data R2DBC framework, which can provide support for repositories, object mapping, and transaction management.

Application Development with MariaDB/Connector/R2DBC (Spring Data)

Overview

Development with MariaDB Connector/R2DBC (Spring) involves building (compiling), and running applications. An Entity class also needs to be created.

Building Applications

// Module Imports
import org.mariadb.r2dbc.MariadbConnectionConfiguration;
import org.mariadb.r2dbc.MariadbConnectionFactory;
import org.springframework.data.r2dbc.core.DatabaseClient;

import reactor.test.StepVerifier;

// Main Application Class
public class App {

   // Connection Configuration
   private static MariadbConnectionConfiguration conf;
   private static MariadbConnectionFactory connFactory;
   private static DatabaseClient client;

   // Main Process
   public static void main(String[] args) {

      try {
         // Configure the Connection
         conf = MariadbConnectionConfiguration.builder()
              .host("192.0.2.1").port(3306)
              .username("db_user").password("db_user_password")
              .database("test").build();

         // Instantiate a Connection Factory
         connFactory = new MariadbConnectionFactory(conf);

         // Instantiate a Database Client
         client = DatabaseClient.create(connFactory);

         // Create a Database Table

         client.execute("CREATE OR REPLACE TABLE test.contact" + "(id INT PRIMARY KEY AUTO_INCREMENT,"
               + "first_name VARCHAR(25)," + "last_name VARCHAR(25)," + "email VARCHAR(25)) ENGINE=InnoDB")
               .fetch()
               .rowsUpdated()
               .as(StepVerifier::create)
               .expectNextCount(1)
               .verifyComplete();

      } catch (IllegalArgumentException e) {
         e.printStackTrace();
      } catch (io.r2dbc.spi.R2dbcNonTransientResourceException e) {
         e.printStackTrace();
      } finally {

      }
   }
}
SHOW TABLES;
+----------------+
| Tables_in_test |
+----------------+
| contact        |
+----------------+
DESC contact;
+------------+-------------+------+-----+--------------------------+
| Field      | Type        | Null | Key | Default  | Extra         |
+------------+-------------+------+-----+----------+---------------+
| id         | int(11)     | NO   | PRI | NULL     | auto_increment|
| first_Name | varchar(25) | YES  |     | NULL     |               |
| last_Name  | varchar(25) | YES  |     | NULL     |               |
| email      | varchar(25) | YES  |     | NULL     |               |
+------------+-------------+------+-----+----------+---------------+
// Module Imports
import org.mariadb.r2dbc.MariadbConnectionConfiguration;
import org.mariadb.r2dbc.MariadbConnectionFactory;
import org.springframework.data.r2dbc.core.DatabaseClient;

import reactor.test.StepVerifier;

// Main Application Class
public class App {
   // Connection Configuration
   private static MariadbConnectionConfiguration conf;
   private static MariadbConnectionFactory connFactory;
   private static DatabaseClient client;

   // Main Process
   public static void main(String[] args) {
      try {
         // Configure the Connection
         conf = MariadbConnectionConfiguration.builder()
              .host("192.0.2.1").port(3306)
              .username("db_user").password("db_user_password")
              .database("test").build();

         // Instantiate a Connection Factory
         connFactory = new MariadbConnectionFactory(conf);

         // Instantiate a Database Client
         client = DatabaseClient.create(connFactory);

         // Truncate Database Table

         client.execute("TRUNCATE TABLE test.contact").fetch()
               .rowsUpdated().as(StepVerifier::create).expectNextCount(1).verifyComplete();
      } catch (IllegalArgumentException e) {
         e.printStackTrace();
      } catch (io.r2dbc.spi.R2dbcNonTransientResourceException e) {
         e.printStackTrace();
      } finally {
      }
   }

}
Batch Operations

Spring Data R2DBC has no built-in support for batch operations as the native API does with the io.r2dbc.spi.Batch class. With Spring Data R2DBC, batch operations can be performed by looping over a List of SQL statements and invoking DatabaseClient.execute(String sql) for each SQL statement.

Code Example: Batching DML and DDL

DML (Data Manipulation Language) refers to all SQL-data statements in the SQL standard (ISO/IEC 9075-2:2016), for example, , , , , and .

DDL (Data Definition Language) refers to all SQL-schema statements in the SQL standard (ISO/IEC 9075-2:2016), for example , , , , and .

The following example shows how to use a batch operation to duplicate the example table created in Setup for Examples:

After running the application App, confirm the table has been created:

This page is: Copyright © 2025 MariaDB. All rights reserved.

USE test;
SHOW TABLES;
+----------------+
| Tables_in_test |
+----------------+
| contact        |
| contact_copy   |
+----------------+
When using Maven to manage your Java builds, running build downloads and installs the relevant JAR dependencies and compiles your project:

Build the package:

Run the application:

Code Example: Create an Entity

Spring Data R2DBC supports object mapping, which allows Java objects to map to rows in the database. This feature can be used to persist objects in the database and read objects from the database.

Before using object mapping, the entity class that models the database table must be defined. The entity class consists of fields matching the database table columns.

For example, entity class is shown below for the test.contact table defined in the Setup for Examples, which contains the id, first_name, last_name, and email columns:

  • The entity class must have the same name as the database table it models.

  • For the test.contact table, the entity class is called Contact.

  • The entity class must declare an identifier (i.e., primary key) field by annotating the field declaration or its getter method declaration with @Id.

This page is: Copyright © 2025 MariaDB. All rights reserved.

Setup for Connector/R2DBC Examples (Spring Data)

Overview

The examples in this MariaDB Connector/R2DBC documentation depend on a database test and table contact.

Create the Schema

Create a test database if one does not exist with the CREATE DATABASE statement:

Create the User

  1. Create a user account to test connectivity with the CREATE USER statement:

  1. Ensure that the user account has privileges to access the tables with the GRANT statement:

This page is: Copyright © 2025 MariaDB. All rights reserved.

//Module Imports
import java.util.Arrays;
import java.util.List;
import org.mariadb.r2dbc.MariadbConnectionConfiguration;
import org.mariadb.r2dbc.MariadbConnectionFactory;
import org.springframework.data.r2dbc.core.DatabaseClient;
import reactor.test.StepVerifier;

// Main Application Class
public class App {
   // Connection Configuration
   private static MariadbConnectionConfiguration conf;
   private static MariadbConnectionFactory connFactory;
   private static DatabaseClient client;

   // Main Process
   public static void main(String[] args) {
      try {
         // Configure the Connection
         conf = MariadbConnectionConfiguration.builder()
              .host("192.0.2.1").port(3306)
              .username("db_user").password("db_user_password")
              .database("test").build();

         // Instantiate a Connection Factory
         connFactory = new MariadbConnectionFactory(conf);

         // Instantiate a Database Client
         client = DatabaseClient.create(connFactory);

         // Create a list or batch of SQL statements
         List<String> batch = Arrays.asList(
               "CREATE DATABASE IF NOT EXISTS test;",
               "CREATE TABLE IF NOT EXISTS test.contact_copy(id INT PRIMARY KEY AUTO_INCREMENT,first_name VARCHAR(50),last_name VARCHAR(50),email VARCHAR(250)) ENGINE=InnoDB;",
               "INSERT INTO test.contact_copy SELECT * FROM test.contact;");

         //Run the batch of SQL statements
         batch.forEach(stmt -> client.execute(stmt)
               .fetch()
               .rowsUpdated()
               .as(StepVerifier::create)
               .expectNextCount(1)
               .verifyComplete());

      } catch (IllegalArgumentException e) {
         e.printStackTrace();
      } catch (io.r2dbc.spi.R2dbcNonTransientResourceException e) {
         e.printStackTrace();
      } finally {

      }
   }
}
DESCRIBE contact_copy;
+------------+-------------+------+-----+--------------------------+
| Field      | Type        | Null | Key | Default  | Extra         |
+------------+-------------+------+-----+----------+---------------+
| id         | int(11)     | NO   | PRI | NULL     | auto_increment|
| first_Name | varchar(25) | YES  |     | NULL     |               |
| last_Name  | varchar(25) | YES  |     | NULL     |               |
| email      | varchar(25) | YES  |     | NULL     |               |
+------------+-------------+------+-----+----------+---------------+
$ mvn package
$ java -jar target/app.jar
// Imports the @Id annotation type, which demarcates an identifier.
import org.springframework.data.annotation.Id;

// This is an Entity class
// It has the same name as the text.contact table
public class Contact {

      // The class members correspond to columns
      // in the test.contact table
      private int id;
      private String first_name;
      private String last_name;
      private String email;

      // Constructor
      public Contact(int id, String first_name, String last_name, String email) {
         this.id = id;
         this.first_name = first_name;
         this.last_name = last_name;
         this.email = email;
      }

      // The @Id annotation indicates that this field
      // is the primary key column
      @Id
      public int getId() {
         return id;
      }

      public String getFirst_name() {
         return first_name;
      }

      public String getLast_name() {
         return last_name;
      }

      public String getEmail() {
         return email;
      }

      @Override
      public String toString() {
         return "Contact [id=" + id + ", first_name=" + first_name + ", last_name=" + last_name + ", email=" + email + "]";
      }
   }
CREATE DATABASE IF NOT EXISTS test;
CREATE USER 'connr2dbc_test'@'192.0.2.50'
   IDENTIFIED BY 'db_user_password';
GRANT ALL PRIVILEGES
   ON test.*
   TO 'connr2dbc_test'@'192.0.2.50';

R2DBC Code Example (Spring Data)

Overview

The following example uses the Spring Data R2DBC framework to select data from the table defined in Setup for Example. Complete information on using Connector/R2DBC with the Spring Data framework is available.

This page is: Copyright © 2025 MariaDB. All rights reserved.

//Module Imports
import org.mariadb.r2dbc.MariadbConnectionConfiguration;
import org.mariadb.r2dbc.MariadbConnectionFactory;
import org.springframework.data.r2dbc.core.DatabaseClient;
import reactor.test.StepVerifier;

// Main Application Class
public class App {
  // Connection Configuration
  private static MariadbConnectionConfiguration conf;
  private static MariadbConnectionFactory connFactory;

  private static DatabaseClient client;

  // Main Process
  public static void main(String[] args) {
     try {
        conf = MariadbConnectionConfiguration.builder()
             !SILO=ent!
             .host("192.0.2.1").port(3306)
             !SILO=sky!
             .host("example.skysql.net").port(5509)
             !END-SILO!
             .username("db_user").password("db_user_password")
             .database("test").build();

        // Instantiate a Connection Factory
        connFactory = new MariadbConnectionFactory(conf);

        // Instantiate a Database Client
        client = DatabaseClient.create(connFactory);

        // Select all rows
        client.select()
           .from(Contact.class)
           .fetch().all()
           .doOnNext(it -> System.out.println(it))
           .as(StepVerifier::create)
           .expectNextCount(3)
           .verifyComplete();

        // Select the first row
        client.select()
           .from(Contact.class)
           .fetch().first()
           .doOnNext(it -> System.out.println(it))
           .as(StepVerifier::create)
           .expectNextCount(1)
           .verifyComplete();

        // Select all rows with explicit query
        client.execute("SELECT id, first_name,last_name,email FROM contact")
           .as(Contact.class)
           .filter(s -> s.fetchSize(25))
           .fetch().all()
           .doOnNext(it -> System.out.println(it))
           .as(StepVerifier::create)
           .expectNextCount(3)
           .verifyComplete();

        // Select single column
        client.execute("SELECT first_name FROM contact")
           .map((row, rowMetadata) -> row.get("first_name", String.class))
           .all()
           .doOnNext(it -> System.out.println(it))
           .as(StepVerifier::create)
           .expectNextCount(3)
           .verifyComplete();

     } catch (IllegalArgumentException e) {
        e.printStackTrace();
     } catch (io.r2dbc.spi.R2dbcNonTransientResourceException e) {
        e.printStackTrace();
     } finally {
     }
  }
}

Install MariaDB Connector/R2DBC (Spring Data)

Overview

Spring Data R2DBC and MariaDB Connector/R2DBC are usually installed using Maven.

It should also be possible to install Spring Data R2DBC and MariaDB Connector/R2DBC by downloading the JAR files. Because Spring Data R2DBC has many dependencies we recommend using Maven, so that dependencies can be resolved by Maven.

Install MariaDB Connector/R2DBC via Maven
  1. To install Spring module for R2DBC, add the spring-boot-starter-data-r2dbc package dependency to your application's pom.xml file:

  1. Since spring boot 3.0, the MariaDB connector is defined as a possible dependency. So setting dependency just needs:

  1. For spring boot before 3.0, a connector compatible with the R2DBC 0.9.1 spec needs to be set in place of org.mariadb:r2dbc-mariadb (be careful not to have any org.mariadb:r2dbc-mariadb dependency set):

  1. To build your application, run Maven:

During the build process, Maven downloads and installs MariaDB Connector/R2DBC and other dependencies from the relevant repositories.

This page is: Copyright © 2025 MariaDB. All rights reserved.

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-r2dbc</artifactId>
   <version>3.3.5</version>
</dependency>
$ mvn package
<dependency>
    <groupId>org.mariadb</groupId>
    <artifactId>r2dbc-mariadb</artifactId>
</dependency>
<dependency>
    <groupId>org.mariadb</groupId>
    <artifactId>r2dbc-mariadb-0.9.1-spec</artifactId>
    <version>1.2.2</version>
</dependency>

Transactions with MariaDB Connector/R2DBC (Spring Data)

Overview

Java developers can use MariaDB Connector/R2DBC to connect to MariaDB database products using the Reactive Relational Database Connectivity (R2DBC) API. R2DBC operations are non-blocking, which makes it more scalable than Java's standard JDBC API. MariaDB Connector/R2DBC can be used with the very popular Spring Data framework, which can provide support for repositories, object mapping, and transaction management.

Transactions

With Spring Data, transactions can be used by utilizing either the R2dbcTransactionManager class or the TransactionAwareConnectionFactoryProxy class. These two classes are mutually exclusive, so they cannot be used together.

Code Example: Transaction Manager

The following example shows how to perform some changes within a reactive streams transaction. The example uses the table created in and the entity class created in .

  • The ReactiveTransactionManager is the central interface in Spring's reactive transaction infrastructure. Applications can use this directly, but it is not primarily meant as an API. Typically, it is recommended that applications use ReactiveTransactionManager with either transactional operators or declarative transaction demarcation through AOP. This example uses it with transactional operators.

  • The R2dbcTransactionManager class is a ReactiveTransactionManager implementation for a single R2DBC ConnectionFactory. This class can be used in any environment with any R2DBC driver, as long as the setup uses a ConnectionFactory as its Connection factory mechanism. The R2dbcTransactionManager class assumes that a separate, independent Connection can be obtained even during an ongoing transaction.

  • The TransactionalOperator is the Operator interface that simplifies programmatic transaction demarcation and transaction exception handling. The central method is transactional, which supports transactional wrapping of functional sequences code. This operator handles the transaction lifecycle and possible exceptions such that neither the ReactiveTransactionCallback implementation nor the calling code needs to explicitly handle transactions.

Code Example: Proxy

The following example shows how to use the TransactionAwareConnectionFactoryProxy class, which is a proxy for a target R2DBC ConnectionFactory, adding awareness of Spring-managed transactions.

The main benefit of the proxy class is that it allows data access code to be used with either the plain R2DBC API or the DatabaseClient. The DatabaseClient gets transaction participation even without a proxy.

  • The DatabaseClient is configured with the proxy connection factory that is aware of Spring managed transactions.

  • The TransactionAwareConnectionFactoryProxy as a proxy must not be used when using the reactive streams transactions.

This page is: Copyright © 2025 MariaDB. All rights reserved.

Setup for Examples
Create the Entity class
//Module Imports
import org.mariadb.r2dbc.MariadbConnectionConfiguration;
import org.mariadb.r2dbc.MariadbConnectionFactory;
import org.springframework.data.r2dbc.connectionfactory.R2dbcTransactionManager;
import org.springframework.data.r2dbc.core.DatabaseClient;
import org.springframework.transaction.ReactiveTransactionManager;
import org.springframework.transaction.reactive.TransactionalOperator;
import reactor.test.StepVerifier;

// Main Application Class
public class App {
   // Connection Configuration
   private static MariadbConnectionConfiguration conf;
   private static MariadbConnectionFactory connFactory;
   private static DatabaseClient client;

   // Main Process
   public static void main(String[] args) {
      try {
         // Configure the Connection
         conf = MariadbConnectionConfiguration.builder()
              .host("192.0.2.1").port(3306)
              .username("db_user").password("db_user_password")
              .database("test").build();

         // Instantiate a Connection Factory
         connFactory = new MariadbConnectionFactory(conf);

         ReactiveTransactionManager tm = new R2dbcTransactionManager(connFactory);

         TransactionalOperator operator = TransactionalOperator.create(tm);

         // Instantiate a Client
         client = DatabaseClient.create(connFactory);

         // Update a contact using the transactional operator
         Contact contact = new Contact(1, "Kai", "Devi", "kai.devi@example.com");

         client.update()
         .table(Contact.class)
         .using(contact)
         .then()
         .as(operator::transactional)
         .as(StepVerifier::create)
         .verifyComplete();

         // Update another contact using the transactional operator
         client.execute("UPDATE test.contact SET email = 'lee.wang@example.com' WHERE id = 2")
         .fetch().rowsUpdated()
         .then()
         .as(operator::transactional)
         .as(StepVerifier::create)
         .expectComplete()
         .verify();

      } catch (IllegalArgumentException e) {
         // ...
      } finally {
         // ...
      }
   }
}
//Module Imports
import org.mariadb.r2dbc.MariadbConnectionConfiguration;
import org.mariadb.r2dbc.MariadbConnectionFactory;
import org.springframework.data.r2dbc.connectionfactory.TransactionAwareConnectionFactoryProxy;
import org.springframework.data.r2dbc.core.DatabaseClient;
import reactor.test.StepVerifier;

// Main Application Class
public class App {
   // Connection Configuration
   private static MariadbConnectionConfiguration conf;
   private static MariadbConnectionFactory connFactory;
   private static DatabaseClient client;

   // Main Process
   public static void main(String[] args) {
      try {
         // Configure the Connection
         conf = MariadbConnectionConfiguration.builder()
              .host("192.0.2.1").port(3306)
              .username("db_user").password("db_user_password")
              .database("test").build();

         // Instantiate a Connection Factory
         connFactory = new MariadbConnectionFactory(conf);

         TransactionAwareConnectionFactoryProxy proxy = new TransactionAwareConnectionFactoryProxy(connFactory);

         // Instantiate a Client
         client = DatabaseClient.create(proxy);

         // Update Data
         Contact contact = new Contact(1, "Kai", "Devi", "kai.devi@example.com");

         client.update().table(Contact.class).using(contact).then().as(StepVerifier::create).verifyComplete();

         client.execute("UPDATE test.contact SET email = 'lee.wang@example.com' WHERE id = 2").fetch().rowsUpdated()
         .then().as(StepVerifier::create).expectComplete().verify();

      } catch (IllegalArgumentException e) {
         // ...
      } finally {
         // ...
      }
   }
}

DML with MariaDB Connector/R2DBC (Spring Data)

Overview

Java developers can use MariaDB Connector/R2DBC to connect to MariaDB database products using the Reactive Relational Database Connectivity (R2DBC) API. R2DBC operations are non-blocking, which makes it more scalable than Java's standard JDBC API. MariaDB Connector/R2DBC can be used with the very popular Spring Data framework, which can provide support for repositories, object mapping, and transaction management.

DML Operations

DML (Data Manipulation Language) refers to all SQL-data statements in the SQL standard (ISO/IEC 9075-2:2016), for example, , , , , and .

With Spring Data, DML operations can be performed by invoking the following methods:

Method
Description

Code Example: INSERT, UPDATE, DELETE

, , and are DML (Data Manipulation Language) operations that modify the data in a table.

The following example shows how to insert data into the example table created in .

To update or delete data, replace the INSERT statement in the code example with an UPDATE, or DELETE statement:

  • To update or delete data, use the update() or delete() methods, instead of the insert() method.

  • To execute a specific DML statement, use the execute() method.

Confirm the data was properly inserted by using MariaDB Client to execute a SELECT statement.

Example output:

Code Example: SELECT

SELECT is a DML (Data Manipulation Language) operation that reads the data from a table.

The following example shows how to select data from the example table created in :

Example output:

This page is: Copyright © 2025 MariaDB. All rights reserved.

DatabaseClient.execute(String sql)

Execute any DML statement.

DatabaseClient.select()

Execute a SELECT statement.

DatabaseClient.insert()

Execute a INSERT statement.

DatabaseClient.update()

Execute a UPDATE statement.

DatabaseClient.delete()

Execute a DELETE statement.

Setup for Examples
Setup for Examples
//Module Imports
import org.mariadb.r2dbc.MariadbConnectionConfiguration;
import org.mariadb.r2dbc.MariadbConnectionFactory;
import org.springframework.data.r2dbc.core.DatabaseClient;

import reactor.test.StepVerifier;

// Main Application Class
public class App {

   // Connection Configuration
   private static MariadbConnectionConfiguration conf;
   private static MariadbConnectionFactory connFactory;

   private static DatabaseClient client;

   // Main Process
   public static void main(String[] args) {

      try {
         conf = MariadbConnectionConfiguration.builder()
              .host("192.0.2.1").port(3306)
              .username("db_user").password("db_user_password")
              .database("test").build();

         // Instantiate a Connection Factory
         connFactory = new MariadbConnectionFactory(conf);

         // Instantiate a Database Client
         client = DatabaseClient.create(connFactory);

         // Add First Contact
         client.insert()
            .into(Contact.class)
            .using(new Contact(1, "Kai", "Devi", "kai.devi@example.com"))
            .then()
            .as(StepVerifier::create)
            .verifyComplete();

         // Add Second Contact
         client.insert()
            .into(Contact.class)
            .using(new Contact(2, "Lee", "Wang", "kai.devi@example.com"))
            .then()
            .as(StepVerifier::create)
            .verifyComplete();

         // Add Third Contact
         client.insert()
            .into(Contact.class)
            .using(new Contact(3, "Dani", "Smith", "dani.smith@example.com"))
            .then()
            .as(StepVerifier::create)
            .verifyComplete();

      } catch (IllegalArgumentException e) {
         e.printStackTrace();
      } catch (io.r2dbc.spi.R2dbcNonTransientResourceException e) {
         e.printStackTrace();
      }
   }
}
SELECT * FROM test.contact;
+----+------------+-----------+------------------------+
| id | first_name | last_name | email                  |
+----+------------+-----------+------------------------+
|  1 | John       | Smith     | john.smith@example.com |
+----+------------+-----------+------------------------+
//Module Imports
import org.mariadb.r2dbc.MariadbConnectionConfiguration;
import org.mariadb.r2dbc.MariadbConnectionFactory;
import org.springframework.data.r2dbc.core.DatabaseClient;
import reactor.test.StepVerifier;

// Main Application Class
public class App {
   // Connection Configuration
   private static MariadbConnectionConfiguration conf;
   private static MariadbConnectionFactory connFactory;

   private static DatabaseClient client;

   // Main Process
   public static void main(String[] args) {
      try {
         conf = MariadbConnectionConfiguration.builder()
              .host("192.0.2.1").port(3306)
              .username("db_user").password("db_user_password")
              .database("test").build();

         // Instantiate a Connection Factory
         connFactory = new MariadbConnectionFactory(conf);

         // Instantiate a Database Client
         client = DatabaseClient.create(connFactory);

         // Select all rows
         client.select()
            .from(Contact.class)
            .fetch().all()
            .doOnNext(it -> System.out.println(it))
            .as(StepVerifier::create)
            .expectNextCount(3)
            .verifyComplete();

         // Select the first row
         client.select()
            .from(Contact.class)
            .fetch().first()
            .doOnNext(it -> System.out.println(it))
            .as(StepVerifier::create)
            .expectNextCount(1)
            .verifyComplete();

         // Select all rows with explicit query
         client.execute("SELECT id, first_name,last_name,email FROM contact")
            .as(Contact.class)
            .filter(s -> s.fetchSize(25))
            .fetch().all()
            .doOnNext(it -> System.out.println(it))
            .as(StepVerifier::create)
            .expectNextCount(3)
            .verifyComplete();

         // Select single column
         client.select()
            .from(Contact.class)
            .map((row, rowMetadata) -> row.get("first_name", String.class))
            .all()
            .doOnNext(it -> System.out.println(it))
            .as(StepVerifier::create)
            .expectNextCount(3)
            .verifyComplete();

      } catch (IllegalArgumentException e) {
         e.printStackTrace();
      } catch (io.r2dbc.spi.R2dbcNonTransientResourceException e) {
         e.printStackTrace();
      }
   }
}
//Output from first query
Contact [id=1, first_name=Kai, last_name=Devi, email=kai.devi@example.com]
Contact [id=2, first_name=Lee, last_name=Wang, email=lee.wang@example.com]
Contact [id=3, first_name=Dani, last_name=Smith, email=dani.smith@example.com]

//Output from second query
Contact [id=1, first_name=Kai, last_name=Devi, email=kai.devi@example.com]

//Output from third query
Contact [id=1, first_name=Kai, last_name=Devi, email=kai.devi@example.com]
Contact [id=2, first_name=Lee, last_name=Wang, email=lee.wang@example.com]
Contact [id=3, first_name=Dani, last_name=Smith, email=dani.smith@example.com]

//Output from fourth query
Kai
Lee
Dani

Repositories with MariaDB Connector/R2DBC (Spring Data)

Overview

Java developers can use MariaDB Connector/R2DBC to connect to MariaDB database products using the Reactive Relational Database Connectivity (R2DBC) API. R2DBC operations are non-blocking, which makes R2DBC more scalable than Java's standard JDBC API. MariaDB Connector/R2DBC can be used with the very popular Spring Data framework, which can provide support for repositories, object mapping, and transaction management.

Spring Data Repositories technology is an abstraction that implements a data access layer over the underlying datastore. Spring Data Repositories reduce the boilerplate code required to access a datastore. Spring Data repositories can be used with the MariaDB/R2DBC connector.

Code Example: Example Application

The following example depends on the environment created in .

Repository Classes Used

In the sections below, we will build an example application that uses a Spring Data Repository. Some annotations that scan packages for repository classes require that the classes be in a named package rather than the default package. The classes in this example application will be in the springdata package.

The example application contains the following classes:

Class
Description

Adapting the Entity

We will need to adapt the entity class for Spring Data Repositories:

The changes are:

  • The entity class is in the springdata package instead of the default package. Spring Data Repositories require repository related classes and interfaces to be in a named package instead of the default package to be able to scan for them.

  • The entity class includes a constructor that defines parameters for all the fields except the identifier id field. * * * This constructor will be used to add new contacts in .

  • The entity class also includes a no-args default constructor.

Create a Repository

The org.springframework.data.repository.reactive.ReactiveCrudRepository interface is the entrypoint for Spring Data R2DBC repositories. The ReactiveCrudRepository interface is used for 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.

The ReactiveCrudRepository interface provides methods listed in following table:

Method
Description

The sample repository class is listed:

  • The findByFirstname(String firstname) method finds entities matching a given first name.

  • The findById(int id) method finds entities matching a given id.

  • An implementation class for the ContactRepository interface is not provided as the Spring Data Repositories framework generates the implementation class as needed.

Create a JavaConfig Configuration

A JavaConfig configuration class is used to enable Spring Data Repositories. A JavaConfig configuration class is a plain old Java object (POJO). A POJO is an ordinary Java object without any special constraints of Java object models or conventions. The sample configuration file used is listed:

  • The configuration class ApplicationConfig extends the AbstractR2dbcConfiguration class and provides only one method connectionFactory(), which is used by the Spring Data Repositories framework to obtain a ConnectionFactory instance to the MariaDB database using a R2DBC driver

  • The ApplicationConfig extends the AbstractR2dbcConfiguration class, which is the base class for Spring Data R2DBC configuration containing bean declarations that must be registered for Spring Data R2DBC.

  • The ApplicationConfig class is annotated with @Configuration, which indicates that a class declares @Bean annotated methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime.

The R2DBC Connection URL format is r2dbc:driver[:protocol]}://[user:password@]host[:port][/path][?option=value.

Create a Service

A service class is used to perform CRUD operations with the Spring Data R2DBC repository. The following develops a service application to test the Spring Data R2DBC repository:

  • To update an existing contact, create a Contact entity instance with the all-args constructor, which is the constructor that defines all fields including the identifier field id as parameters. Subsequently, call method ReactiveCrudRepository.save(S entity) to save the entity. To verify that the contact has been updated call the ReactiveCrudRepository.findByFirstname method.

Test the Service

Run the service class springdata.RepositoryService and for the sample table data and the sample application the following output is made:

Run a SQL query to verify the test.contact table data we started with got deleted and three new contacts are added:

This page is: Copyright © 2025 MariaDB. All rights reserved.

findAllById(Iterable ids)

Returns all instances of the type T with the given ids. If some or all ids are not found, no entities are returned for these ids. Note that the order of elements in the result is not guaranteed. It returns a Flux emitting the found entities. The size can be equal or less than the number of given ids.

findAllById(Publisher idStream)

Returns all instances of the type T with the given ids supplied by a Publisher. If some or all ids are not found, no entities are returned for these ids. Note that the order of elements in the result is not guaranteed. It returns a Flux emitting the found entities.

count()

Returns the number of entities available. It returns a Mono emitting the number of entities.

deleteById(ID id)

Deletes the entity with the given id. It returns a Mono signaling when operation has completed.

deleteById(Publisher id)

Deletes the entity with the given id supplied by a Publisher. It returns a Mono signaling when operation has completed.

delete(T entity)

Deletes a given entity. It returns a Mono signaling when operation has completed.

deleteAll(Iterable<? extends T> entities)

Deletes the given entities. It returns a Mono signaling when operation has completed.

deleteAll(Publisher<? extends T> entityStream)

Deletes the given entities supplied by a Publisher. It returns a Mono signaling when operation has completed.

deleteAll()

Deletes all entities managed by the repository. It returns a Mono signaling when operation has completed.

The ApplicationConfig class is annotated with @EnableR2dbcRepositories, which indicates the reactive relational repositories should be activated using R2DBC. A base package class is specified as ContactRepository.class using the basePackageClasses annotation attribute. If no base package is configured through either value(), basePackages(), or basePackageClasses() it will scan the package of the annotated class.
  • The ApplicationConfig class is annotated with @ComponentScan, which configures component scanning directives for use with @Configuration classes. A base package class is specified as RepositoryService.class using the basePackageClasses annotation attribute. With the @ComponentScan set, the RepositoryService.class class is used within the Spring Data Repositories framework. If no base package is configured through either value(), basePackages(), or basePackageClasses() it will scan the package of the annotated class, which must be a named package and not the default package.

  • The connectionFactory() method returns a ConnectionFactory instance and is annotated with @Bean to indicate that the method produces a bean to be managed by the Spring container. A new ConnectionFactory is created using the static method ConnectionFactories.get(String url).

  • springdata.ApplicationConfig

    The JavaConfig configuration class that enables Spring Data repositories.

    springdata.Contact

    The Entity class that models the table.

    springdata.ContactRepository

    The Repository interface.

    springdata.RepositoryService

    The Service class that performs CRUD (Create, Read, Update Delete) operations.

    save(S entity)

    Saves a given entity. Returns a Mono for the saved entity. Use the returned instance for further operations as the save operation might have changed the entity instance completely. The save(S entity) method updates an existing entity if the Entity object arguments include the identifier field. The save(S entity) method adds a new entity if the Entity object arguments do not include the identifier field.

    saveAll(Iterable entities)

    Saves all given entities. It returns a Flux emitting the saved entities.

    saveAll(Publisher entityStream)

    Saves all given entities. It returns a Flux emitting the saved entities..

    findById(ID id)

    Retrieves an entity by its id. It returns a Mono emitting the entity with the given id or Mono.empty() if none found.

    findById(Publisher id)

    Retrieves an entity by its id supplied by a Publisher. It returns a Mono emitting the entity with the given id or Mono.empty() if none found.

    findAll()

    Returns all instances of the type. It returns a Flux emitting all entities.

    Setup for Examples
    previously created
    Creating a Service Class
    package springdata;
    
    // Imports the @Id annotation type, which demarcates an identifier.
    //Module Imports
    import org.springframework.data.annotation.Id;
    
    // This is an Entity class
    // It has the same name as the text.contact table
    public class Contact {
    
       // The class members correspond to columns
       // in the test.contact table
       private int id;
       private String first_name;
       private String last_name;
       private String email;
    
       // Constructor
       public Contact() {
       }
    
       // Constructor
       public Contact(String first_name, String last_name, String email) {
    
          this.first_name = first_name;
          this.last_name = last_name;
          this.email = email;
       }
    
       // Constructor
       public Contact(int id, String first_name, String last_name, String email) {
          this.id = id;
          this.first_name = first_name;
          this.last_name = last_name;
          this.email = email;
       }
    
       // The @Id annotation indicates that this field
       // is the primary key column
       @Id
       public int getId() {
          return id;
       }
    
       public String getFirst_name() {
          return first_name;
       }
    
       public String getLast_name() {
          return last_name;
       }
    
       public String getEmail() {
          return email;
       }
    
       @Override
       public String toString() {
          return "Contact [id=" + id + ", first_name=" + first_name + ", last_name=" + last_name + ", email=" + email
                + "]";
       }
    }
    package springdata;
    
    //Module Imports
    import reactor.core.publisher.Flux;
    import org.springframework.data.r2dbc.repository.Query;
    import org.springframework.data.repository.reactive.ReactiveCrudRepository;
    
    // ReactiveCrudRepository<Contact, Integer>:
    //   Entity Class: Contact
    //   Data type of identifier: Integer
    interface ContactRepository extends ReactiveCrudRepository<Contact, Integer> {
    
       // The Query annotation provides an SQL statement corresponding to the method
       @Query("select id, first_name, last_name, email from contact c where c.first_name = :first_name")
       Flux<Contact> findByFirstname(String firstname);
    
       @Query("select id, first_name, last_name, email from contact c where c.id = :id")
       Flux<Contact> findById(int id);
    }
    package springdata;
    
    //Module Imports
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration;
    import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories;
    
    import io.r2dbc.spi.ConnectionFactories;
    import io.r2dbc.spi.ConnectionFactory;
    
    @Configuration
    @EnableR2dbcRepositories(basePackageClasses = ContactRepository.class)
    @ComponentScan(basePackageClasses = RepositoryService.class)
    class ApplicationConfig extends AbstractR2dbcConfiguration {
       @Bean
       public ConnectionFactory connectionFactory() {
          return ConnectionFactories.get("r2dbc:mariadb://connr2dbc_test:db_user_password@192.0.2.50:3306/test");
       }
    }
    package springdata;
    
    //Module Imports
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.stereotype.Service;
    
    import reactor.test.StepVerifier;
    
    // The @Service annotation indicates that the class is a "Service".
    // Spring Data Repositories framework auto-discovers the service class
    // through classpath scanning because we have set the @ComponentScan
    // annotation in ApplicationConfig to scan for RepositoryService.class.
    @Service
    public class RepositoryService {
    
       // The @Autowired annotation indicates that the field is to be autowired
       // by Spring's dependency injection facilities.
       @Autowired
       private static ContactRepository repository;
    
       // The ApplicationContext provides Bean factory methods for
       // accessing application components.
       private static ApplicationContext ctx;
    
       public static void main(String[] args) {
    
          try {
             // The AnnotationConfigApplicationContext class is a standalone application context,
             // accepting component classes as input, in particular @Configuration-annotated
             // classes such as the ApplicationConfig class we developed.
             ctx = new AnnotationConfigApplicationContext(ApplicationConfig.class);
    
             // Returns the bean instance that uniquely matches the ContactRepository.class
             repository = ctx.getBean(ContactRepository.class);
             RepositoryService repoService = new RepositoryService();
             repoService.crud();
          } catch (Exception e) {
             System.out.println();
          } finally {
             // ...
          }
       }
    
       public void crud() {
    
          // Print number of rows
          System.out.println("Number of contacts in database is " + repository.count().block());
    
          // Delete all data
          repository.deleteAll().block();
    
          // Print number of rows again
          System.out.println("Number of contacts in database is " + repository.count().block());
    
          // Insert one row
          // ID is auto-generated
          Contact contact = new Contact("John", "Smith", "john.smith@gmail.com");
          repository.save(contact)
             .as(StepVerifier::create)
             .expectNextCount(1)
             .verifyComplete();
    
          // Insert another row
          // ID is auto-generated
          contact = new Contact("Johnny", "Smith", "johnny.smith@gmail.com");
          repository.save(contact)
             .as(StepVerifier::create)
             .expectNextCount(1)
             .verifyComplete();
    
          // Insert another row
          // ID is auto-generated
          contact = new Contact("Joe", "Smith", "joe.smith@gmail.com");
          repository.save(contact)
             .as(StepVerifier::create)
             .expectNextCount(1)
             .verifyComplete();
    
          // Print all rows
          repository.findAll()
             .doOnNext(it -> System.out.println(it)).as(StepVerifier::create)
             .expectNextCount(3)
             .verifyComplete();
    
          // Print rows with first name "John"
          repository.findByFirstname("John")
             .doOnNext(it -> System.out.println(it))
             .as(StepVerifier::create)
             .expectNextCount(1)
             .verifyComplete();
    
          // Print row with ID 1
          repository.findById(1)
             .doOnNext(it -> System.out.println(it))
             .as(StepVerifier::create)
             .expectNextCount(1)
             .verifyComplete();
    
           // Update email for contact with id 1
          // ID is explicitly provided
          contact = new Contact(1, "John", "Smith", "johnsmith@gmail.com");
          repository.save(contact)
             .as(StepVerifier::create)
             .expectNextCount(1)
             .verifyComplete();
    
          // Print rows with first name "John"
          repository.findByFirstname("John")
             .doOnNext(it -> System.out.println(it))
             .as(StepVerifier::create)
             .expectNextCount(1)
             .verifyComplete();
       }
    }
    Number of contacts in database is 0
    Number of contacts in database is 0
    Contact [id=1, first_name=John, last_name=Smith, email=john.smith@example.com]
    Contact [id=2, first_name=Johnny, last_name=Smith, email=johnny.smith@example.com]
    Contact [id=3, first_name=Joe, last_name=Smith, email=joe.smith@example.com]
    Contact [id=1, first_name=John, last_name=Smith, email=john.smith@example.com]
    Contact [id=1, first_name=John, last_name=Smith, email=john.smith@example.com]
    Contact [id=1, first_name=John, last_name=Smith, email=johnsmith@example.com]
    SELECT * FROM test.contact;
    +----+------------+-----------+---------------------------+
    | id | first_name | last_name | email                     |
    +----+------------+-----------+---------------------------+
    |  1 | John       | Smith     | johnsmith@example.com     |
    +----+------------+-----------+---------------------------+
    |  2 | Johnny     | Smith     | johnny.smith@example.com  |
    +----+------------+-----------+---------------------------+
    |  3 | Joe        | Smith     | joe.smith@example.com     |
    +----+------------+-----------+---------------------------+
    ALTER TABLE
    CREATE TABLE
    DROP TABLE
    CREATE DATABASE
    TRUNCATE TABLE
    DELETE
    INSERT
    REPLACE
    SELECT
    UPDATE
    ALTER TABLE
    CREATE TABLE
    DROP TABLE
    CREATE DATABASE
    TRUNCATE TABLE
    DELETE
    INSERT
    REPLACE
    SELECT
    UPDATE
    INSERT
    UPDATE
    DELETE