R2DBC
This module provides integration between Micronaut and R2DBC.
For this project, you can find a list of releases (with release notes) here:
The quickest way to get started is to create a new Micronaut application with Micronaut Launch and choose the data-r2dbc, mysql and flyway features. This can also be done via the Micronaut 2.2 and above CLI:
Creating an application with the CLI
# For Maven add: --build maven
$ mn create-app --lang java example --features data-r2dbc,flyway,mysql
Creating an application with curl
# For Maven add to the URL: &build=maven
$ curl https://launch.micronaut.io/demo.zip?lang=java&features=data-r2dbc,flyway,mysql -o demo.zip && unzip demo.zip -d demo && cd demo
The generated application will use MySQL since we passed the mysql feature adding dependency on the R2DBC driver for MySQL:
runtimeOnly("dev.miku:r2dbc-mysql")
<dependency>
<groupId>dev.miku</groupId>
<artifactId>r2dbc-mysql</artifactId>
<scope>runtime</scope>
</dependency>
And for flyway the JDBC driver:
runtimeOnly("mysql:mysql-connector-java")
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
|
Tip
|
To create configurations for other drivers you can select the appropriate feature: oracle, postgres, sqlserver, h2 or mariadb.
|
Now define a SQL script that creates your initial schema in src/main/resources/db/migration. For example:
Example V1__create-schema.sql
CREATE TABLE book(id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255), pages INT, author_id BIGINT NOT NULL);
CREATE TABLE author(id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255));
You can now configure your application to connect to the database using src/main/resources/application.yml which contains the application configuration:
Example application.yml
flyway: #
datasources:
default:
enabled: true
datasources:
default: #
url: ${JDBC_URL:`jdbc:mysql://localhost:3306/mydatabase`}
r2dbc:
datasources:
default: #
url: r2dbc:mysql:///mydatabase
host: ${R2DBC_HOST:localhost} #
port: ${R2DBC_PORT:3306}
|
Tip
|
The R2DBC ConnectionFactory object can be injected anywhere in your code with dependency injection.
|
Now define a @MappedEntity that maps to the author table defined in the schema:
package example;
import io.micronaut.data.annotation.*;
import io.micronaut.serde.annotation.Serdeable;
@Serdeable
@MappedEntity
public class Author {
@GeneratedValue
@Id
private Long id;
private final String name;
public Author(String name) {
this.name = name;
}
public String getName() {
return name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
package example
import io.micronaut.data.annotation.GeneratedValue
import io.micronaut.data.annotation.Id
import io.micronaut.data.annotation.MappedEntity
import io.micronaut.serde.annotation.Serdeable
@Serdeable
@MappedEntity
data class Author(val name: String) {
@GeneratedValue
@Id
var id: Long? = null
}
package example
import io.micronaut.data.annotation.*
import io.micronaut.serde.annotation.Serdeable
@Serdeable
@MappedEntity
class Author {
@GeneratedValue
@Id
Long id
final String name
Author(String name) {
this.name = name
}
}
And a repository interface to access the database that extends from ReactiveStreamsRepository:
package example;
import org.jspecify.annotations.NonNull;
import io.micronaut.data.model.query.builder.sql.Dialect;
import io.micronaut.data.r2dbc.annotation.R2dbcRepository;
import io.micronaut.data.repository.reactive.ReactiveStreamsCrudRepository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import jakarta.validation.constraints.NotNull;
@R2dbcRepository(dialect = Dialect.MYSQL) //
public interface AuthorRepository extends ReactiveStreamsCrudRepository<Author, Long> {
@NonNull
@Override
Mono<Author> findById(@NonNull @NotNull Long aLong); //
@NonNull
@Override
Flux<Author> findAll();
}
package example
import io.micronaut.data.model.query.builder.sql.Dialect
import io.micronaut.data.r2dbc.annotation.R2dbcRepository
import io.micronaut.data.repository.reactive.ReactiveStreamsCrudRepository
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import jakarta.validation.constraints.NotNull
@R2dbcRepository(dialect = Dialect.MYSQL) //
interface AuthorRepository : ReactiveStreamsCrudRepository<Author, Long> {
override fun findById(id: @NotNull Long): Mono<Author> //
override fun findAll(): Flux<Author>
}
package example
import org.jspecify.annotations.NonNull
import io.micronaut.data.model.query.builder.sql.Dialect
import io.micronaut.data.r2dbc.annotation.R2dbcRepository
import io.micronaut.data.repository.reactive.ReactiveStreamsCrudRepository
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import jakarta.validation.constraints.NotNull
@R2dbcRepository(dialect = Dialect.MYSQL) //
interface AuthorRepository extends ReactiveStreamsCrudRepository<Author, Long> {
@NonNull
@Override
Mono<Author> findById(@NonNull @NotNull Long aLong) //
@NonNull
@Override
Flux<Author> findAll()
}
You can now inject this interface into controllers and use it to perform R2DBC queries:
package example;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Controller("/authors")
public class AuthorController {
private final AuthorRepository repository;
public AuthorController(AuthorRepository repository) {
this.repository = repository;
}
@Get
Flux<Author> all() { //
return repository.findAll();
}
@Get("/id")
Mono<Author> get(Long id) { //
return repository.findById(id);
}
}
package example
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
@Controller("/authors")
class AuthorController(private val repository: AuthorRepository) {
@Get
fun all(): Flux<Author> { //
return repository.findAll()
}
@Get("/id")
fun get(id: Long): Mono<Author> { //
return repository.findById(id)
}
}
package example
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
@Controller("/authors")
class AuthorController {
private final AuthorRepository repository
AuthorController(AuthorRepository repository) {
this.repository = repository
}
@Get
Flux<Author> all() { //
return repository.findAll()
}
@Get("/id")
Mono<Author> get(Long id) { //
return repository.findById(id)
}
}
The following drivers are available as of this writing.
H2
runtimeOnly("io.r2dbc:r2dbc-h2")
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-h2</artifactId>
<scope>runtime</scope>
</dependency>
And for Flyway migrations the JDBC driver:
runtimeOnly("com.h2database:h2")
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
MySQL
runtimeOnly("dev.miku:r2dbc-mysql")
<dependency>
<groupId>dev.miku</groupId>
<artifactId>r2dbc-mysql</artifactId>
<scope>runtime</scope>
</dependency>
And for Flyway migrations the JDBC driver:
runtimeOnly("mysql:mysql-connector-java")
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
MariaDB
runtimeOnly("org.mariadb:r2dbc-mariadb")
<dependency>
<groupId>org.mariadb</groupId>
<artifactId>r2dbc-mariadb</artifactId>
<scope>runtime</scope>
</dependency>
And for Flyway migrations the JDBC driver:
runtimeOnly("org.mariadb.jdbc:mariadb-java-client")
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<scope>runtime</scope>
</dependency>
Postgresql
runtimeOnly("org.postgresql:r2dbc-postgresql")
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>r2dbc-postgresql</artifactId>
<scope>runtime</scope>
</dependency>
|
Note
|
For GraalVM native images, use a recent org.postgresql:r2dbc-postgresql version. Older 0.9.x releases do not include the native-image reflection metadata required by the PostgreSQL codec layer and can fail at runtime with errors about array types such as Instant[]. The Micronaut R2DBC BOM manages a compatible version for you, but if you override the driver version manually, use 1.0.2.RELEASE or newer.
|
And for Flyway migrations the JDBC driver:
runtimeOnly("org.postgresql:postgresql")
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
Google Cloud SQL
To use PostgreSQL through the Google Cloud SQL R2DBC connector, add the Cloud SQL connector dependency alongside the PostgreSQL R2DBC driver:
runtimeOnly("com.google.cloud.sql:cloud-sql-connector-r2dbc-postgres")
<dependency>
<groupId>com.google.cloud.sql</groupId>
<artifactId>cloud-sql-connector-r2dbc-postgres</artifactId>
<scope>runtime</scope>
</dependency>
Use a full Cloud SQL URL for the datasource and provide credentials separately (for example via environment variables or secret injection):
r2dbc:
datasources:
default:
url: r2dbc:gcp:postgres://<CLOUD_SQL_CONNECTION_NAME>/<DATABASE_NAME>
username: ${DB_USER}
password: ${DB_PASS}
If you also want R2DBC pooling, compose the delegated driver layers in the URL instead of trying to split gcp and pool across separate driver and options.driver properties:
r2dbc:
datasources:
default:
url: r2dbc:pool:gcp:postgres://<CLOUD_SQL_CONNECTION_NAME>/<DATABASE_NAME>
username: ${DB_USER}
password: ${DB_PASS}
The protocol segment accepts both postgres and postgresql.
SQL Server
runtimeOnly("io.r2dbc:r2dbc-mssql")
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-mssql</artifactId>
<scope>runtime</scope>
</dependency>
And for Flyway migrations the JDBC driver:
runtimeOnly("com.microsoft.sqlserver:mssql-jdbc")
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<scope>runtime</scope>
</dependency>
Micronaut Data builds on this module and provides support for Reactive repositories. See the documentation on Micronaut Data R2DBC for more information and the Quick Start section for a demonstration on how to create an application.
You can find the source code of this project in this repository: