#25 - Add reference documentation.

Original pull request: #28.
This commit is contained in:
Mark Paluch
2018-11-26 15:03:26 +01:00
parent 0aa623280f
commit a6140b8ba8
10 changed files with 1107 additions and 0 deletions

View File

@@ -0,0 +1,37 @@
= Spring Data R2DBC - Reference Documentation
Mark Paluch
:revnumber: {version}
:revdate: {localdate}
:toc:
:toc-placement!:
:linkcss:
:doctype: book
:docinfo: shared
:source-highlighter: prettify
:icons: font
:imagesdir: images
ifdef::backend-epub3[:front-cover-image: image:epub-cover.png[Front Cover,1050,1600]]
:spring-data-commons-docs: ../../../../spring-data-commons/src/main/asciidoc
:rr2dbcVersion: 1.0.0.M6
:reactiveStreamsVersion: 1.0.1
:reactiveStreamsJavadoc: http://www.reactive-streams.org/reactive-streams-{reactiveStreamsVersion}-javadoc
(C) 2018 The original authors.
NOTE: Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.
toc::[]
include::preface.adoc[]
include::new-features.adoc[leveloffset=+1]
include::{spring-data-commons-docs}/dependencies.adoc[leveloffset=+1]
include::{spring-data-commons-docs}/repositories.adoc[leveloffset=+1]
[[reference]]
= Reference Documentation
include::reference/introduction.adoc[leveloffset=+1]
include::reference/r2dbc.adoc[leveloffset=+1]
include::reference/r2dbc-repositories.adoc[leveloffset=+1]
include::reference/mapping.adoc[leveloffset=+1]

View File

@@ -0,0 +1,10 @@
[[new-features]]
= New & Noteworthy
[[new-features.1-0-0-M1]]
== What's New in Spring Data R2DBC 1.0.0 M1
* Initial R2DBC support through `DatabaseClient`
* Initial Transaction support through `TransactionalDatabaseClient`
* Initial R2DBC Repository Support through `R2dbcRepository`
* Initial Dialect support for Postgres and Microsoft SQL Server

View File

@@ -0,0 +1,117 @@
[[preface]]
= Preface
The Spring Data R2DBC project applies core Spring concepts to the development of solutions that use the https://r2dbc.io[R2DBC] drivers for relational databases.
We provide a `DatabaseClient` as a high-level abstraction for storing and querying rows.
This document is the reference guide for Spring Data - R2DBC Support.
It explains R2DBC module concepts and semantics and.
This section provides some basic introduction to Spring and databases.
[[get-started:first-steps:spring]]
== Learning Spring
Spring Data uses Spring framework's http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/core.html[core] functionality, including:
* http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/core.html#beans[IoC] container
* http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/core.html#validation[type conversion system]
* http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/core.html#expressions[expression language]
* http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/integration.html#jmx[JMX integration]
* http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/data-access.html#dao-exceptions[DAO exception hierarchy].
While you need not know the Spring APIs, understanding the concepts behind them is important.
At a minimum, the idea behind Inversion of Control (IoC) should be familiar, and you should be familiar with whatever IoC container you choose to use.
The core functionality of the R2DBC support can be used directly, with no need to invoke the IoC services of the Spring Container.
This is much like `JdbcTemplate`, which can be used "'standalone'" without any other services of the Spring container.
To leverage all the features of Spring Data R2DBC, such as the repository support, you need to configure some parts of the library to use Spring.
To learn more about Spring, you can refer to the comprehensive documentation that explains the Spring Framework in detail.
There are a lot of articles, blog entries, and books on the subject.
See the Spring framework http://spring.io/docs[home page] for more information.
[[get-started:first-steps:what]]
== What is R2DBC?
https://r2dbc.io[R2DBC] is the acronym for Reactive Relational Database Connectivity. R2DBC is an API specification initiative that declares a reactive API to be implemented by driver vendors for accessing their relational databases.
Part of the answer why R2DBC was created is the need for a non-blocking application stack to handle concurrency with a small number of threads and scale with fewer hardware resources.
This need cannot be satisfied with reusing standardized relational database access APIs - namely JDBC as JDBC is a fully blocking API.
Attempts to compensate for blocking behavior with a `ThreadPool` are limited useful.
The other part of the answer is that most applications use a relational database to store their data.
While several NoSQL database vendors provide reactive database clients for their databases, migration to NoSQL is not an option for most projects.
This was the motivation for a new common API to serve as a foundation for any non-blocking database driver.
While the open source ecosystem hosts various non-blocking relational database driver implementations, each client comes with a vendor-specific API so a generic layer on top of these libraries is not possible.
[[get-started:first-steps:reactive]]
== What is Reactive?
The term, reactive refers to programming models that are built around reacting to change, availability, and processabilitynetwork components reacting to I/O events, UI controllers reacting to mouse events, resources being made available and others.
In that sense, non-blocking is reactive, because, instead of being blocked, we are now in the mode of reacting to notifications as operations complete or data becomes available.
There is also another important mechanism that we on the Spring team associate with reactive and that is non-blocking back pressure.
In synchronous, imperative code, blocking calls serve as a natural form of back pressure that forces the caller to wait.
In non-blocking code, it becomes important to control the rate of events so that a fast producer does not overwhelm its destination.
Reactive Streams is a https://github.com/reactive-streams/reactive-streams-jvm/blob/v{reactiveStreamsVersion}/README.md#specification[small spec] (also https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/Flow.html[adopted] in Java 9) that defines the interaction between asynchronous components with back pressure.
For example a data repository (acting as {reactiveStreamsJavadoc}/org/reactivestreams/Publisher.html[`Publisher`]) can produce data that an HTTP server (acting as {reactiveStreamsJavadoc}/org/reactivestreams/Subscriber.html`[`Subscriber`]) can then write to the response.
The main purpose of Reactive Streams is to let the subscriber to control how quickly or how slowly the publisher produces data.
[[get-started:first-steps:reactive-api]]
== Reactive API
Reactive Streams plays an important role for interoperability. It is of interest to libraries and infrastructure components but less useful as an application API, because it is too low-level.
Applications need a higher-level and richer, functional API to compose async logicsimilar to the Java 8 Stream API but not only for tables.
This is the role that reactive libraries play.
https://github.com/reactor/reactor[Project Reactor] is the reactive library of choice for Spring Data R2DBC.
It provides the https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html[`Mono`] and https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Flux.html[`Flux`] API types to work on data sequences of `0..1` (`Mono`) and `0..N` (`Flux`) through a rich set of operators aligned with the ReactiveX vocabulary of operators.
Reactor is a Reactive Streams library and, therefore, all of its operators support non-blocking back pressure.
Reactor has a strong focus on server-side Java. It is developed in close collaboration with Spring.
Spring Data R2DBC requires Project Reactor as a core dependency but it is interoperable with other reactive libraries via Reactive Streams.
As a general rule, a Spring Data R2DBC repository accepts a plain `Publisher` as input, adapts it to a Reactor type internally, uses that, and returns either a `Mono` or a `Flux` as output.
So, you can pass any `Publisher` as input and you can apply operations on the output, but you need to adapt the output for use with another reactive library.
Whenever feasible, Spring Data adapts transparently to the use of RxJava or another reactive library.
[[requirements]]
== Requirements
The Spring Data R2DBC 1.x binaries require:
* JDK level 8.0 and above
* http://spring.io/docs[Spring Framework] {springVersion} and above
* R2DBC {r2dbcVersion} and above
[[get-started:help]]
== Additional Help Resources
Learning a new framework is not always straightforward.
In this section, we try to provide what we think is an easy-to-follow guide for starting with the Spring Data R2DBC module.
However, if you encounter issues or you need advice, feel free to use one of the following links:
[[get-started:help:community]]
Community Forum :: Spring Data on http://stackoverflow.com/questions/tagged/spring-data[Stack Overflow] is a tag for all Spring Data (not just R2DBC) users to share information and help each other.
Note that registration is needed only for posting.
[[get-started:help:professional]]
Professional Support :: Professional, from-the-source support, with guaranteed response time, is available from http://pivotal.io/[Pivotal Sofware, Inc.], the company behind Spring Data and Spring.
[[get-started:up-to-date]]
== Following Development
For information on the Spring Data R2DBC source code repository, nightly builds, and snapshot artifacts, see the Spring Data R2DBC http://projects.spring.io/spring-data-r2dbc/[homepage].
You can help make Spring Data best serve the needs of the Spring community by interacting with developers through the Community on http://stackoverflow.com/questions/tagged/spring-data[Stack Overflow].
To follow developer activity, look for the mailing list information on the Spring Data R2DBC https://projects.spring.io/spring-data-r2dbc/[homepage].
If you encounter a bug or want to suggest an improvement, please create a ticket on the Spring Data R2DBC https://github.com/spring-projects/spring-data-r2dbc/issues[issue tracker].
To stay up to date with the latest news and announcements in the Spring eco system, subscribe to the Spring Community http://spring.io[Portal].
You can also follow the Spring http://spring.io/blog[blog] or the Spring Data project team on Twitter (http://twitter.com/SpringData[SpringData]).
== Project Metadata
* Version control: http://github.com/spring-projects/spring-data-r2dbc
* Bugtracker: https://github.com/spring-projects/spring-data-r2dbc/issues
* Release repository: https://repo.spring.io/libs-release
* Milestone repository: https://repo.spring.io/libs-milestone
* Snapshot repository: https://repo.spring.io/libs-snapshot

View File

@@ -0,0 +1,10 @@
[[introduction]]
= Introduction
== Document Structure
This part of the reference documentation explains the core functionality offered by Spring Data R2DBC.
"`<<r2dbc.core>>`" introduces the R2DBC module feature set.
"`<<r2dbc.repositories>>`" introduces the repository support for R2DBC.

View File

@@ -0,0 +1,105 @@
[[mapping-chapter]]
= Mapping
Rich mapping support is provided by the `MappingR2dbcConverter`. `MappingR2dbcConverter` has a rich metadata model that allows to map domain objects to a data row.
The mapping metadata model is populated by using annotations on your domain objects.
However, the infrastructure is not limited to using annotations as the only source of metadata information.
The `MappingR2dbcConverter` also lets you map objects to rows without providing any additional metadata, by following a set of conventions.
This section describes the features of the `MappingR2dbcConverter`, including how to use conventions for mapping objects to rows and how to override those conventions with annotation-based mapping metadata.
[[mapping-conventions]]
== Convention-based Mapping
`MappingR2dbcConverter` has a few conventions for mapping objects to rows when no additional mapping metadata is provided.
The conventions are:
* The short Java class name is mapped to the table name in the following manner.
The class `com.bigbank.SavingsAccount` maps to the `savings_account` table name.
* Nested objects are not supported.
* The converter uses any Spring Converters registered with it to override the default mapping of object properties to row columns and values.
* The fields of an object are used to convert to and from columns in the row.
Public `JavaBean` properties are not used.
* If you have a single non-zero-argument constructor whose constructor argument names match top-level column names of the row, that constructor is used.
Otherwise, the zero-argument constructor is used.
If there is more than one non-zero-argument constructor, an exception will be thrown.
[[mapping-usage]]
== Metadata-based Mapping
To take full advantage of the object mapping functionality inside the Spring Data R2DBC support, you should annotate your mapped objects with the `@Table` annotation.
Although it is not necessary for the mapping framework to have this annotation (your POJOs are mapped correctly, even without any annotations), it lets the classpath scanner find and pre-process your domain objects to extract the necessary metadata.
If you do not use this annotation, your application takes a slight performance hit the first time you store a domain object, because the mapping framework needs to build up its internal metadata model so that it knows about the properties of your domain object and how to persist them.
The following example shows a domain object:
.Example domain object
====
[source,java]
----
package com.mycompany.domain;
@Table
public class Person {
@Id
private Long id;
private Integer ssn;
private String firstName;
@Indexed
private String lastName;
}
----
====
IMPORTANT: The `@Id` annotation tells the mapper which property you want to use for the primary key property.
[[mapping-usage-annotations]]
=== Mapping Annotation Overview
The `MappingR2dbcConverter` can use metadata to drive the mapping of objects to rows. The following annotations are available:
* `@Id`: Applied at the field level to mark the primary used for identity purpose.
* `@Table`: Applied at the class level to indicate this class is a candidate for mapping to the database. You can specify the name of the table where the database will be stored.
* `@Transient`: By default all private fields are mapped to the row, this annotation excludes the field where it is applied from being stored in the database
* `@PersistenceConstructor`: Marks a given constructor - even a package protected one - to use when instantiating the object from the database. Constructor arguments are mapped by name to the key values in the retrieved row.
* `@Column`: Applied at the field level and described the name of the column as it will be represented in the row thus allowing the name to be different than the fieldname of the class.
The mapping metadata infrastructure is defined in a separate spring-data-commons project that is technology agnostic. Specific subclasses are using in the R2DBC support to support annotation based metadata. Other strategies are also possible to put in place if there is demand.
[[mapping-custom-object-construction]]
=== Customized Object Construction
The mapping subsystem allows the customization of the object construction by annotating a constructor with the `@PersistenceConstructor` annotation. The values to be used for the constructor parameters are resolved in the following way:
* If a parameter is annotated with the `@Value` annotation, the given expression is evaluated and the result is used as the parameter value.
* If the Java type has a property whose name matches the given field of the input row, then it's property information is used to select the appropriate constructor parameter to pass the input field value to. This works only if the parameter name information is present in the java `.class` files which can be achieved by compiling the source with debug information or using the new `-parameters` command-line switch for javac in Java 8.
* Otherwise a `MappingException` will be thrown indicating that the given constructor parameter could not be bound.
[source,java]
----
class OrderItem {
private @Id String id;
private int quantity;
private double unitPrice;
OrderItem(String id, int quantity, double unitPrice) {
this.id = id;
this.quantity = quantity;
this.unitPrice = unitPrice;
}
// getters/setters ommitted
}
----

View File

@@ -0,0 +1,121 @@
[[r2dbc.repositories]]
= R2DBC Repositories
[[r2dbc.repositories.intro]]
== Introduction
This chapter points out the specialties for repository support for R2DBC.
This chapter builds on the core repository support explained in <<repositories>>.
Before reading this chapter, you should have a sound understanding of the basic concepts explained there.
[[r2dbc.repositories.usage]]
== Usage
To access domain entities stored in a relational database, you can use our sophisticated repository support that eases implementation quite significantly.
To do so, create an interface for your repository, as the following example shows:
.Sample Person entity
====
[source,java]
----
public class Person {
@Id
private Long id;
private String firstname;
private String lastname;
// … getters and setters omitted
}
----
====
.Basic repository interface to persist Person entities
====
[source]
----
public interface PersonRepository extends ReactiveCrudRepository<Person, Long> {
// additional custom query methods go here
}
----
====
Right now, this interface serves only to provide type information, but we can add additional methods to it later.
To configure R2DBC repositories, you can use the `@EnableR2dbcRepositories` annotation.
If no base package is configured, the infrastructure scans the package of the annotated configuration class.
The following example shows how to use Java configuration for a repository:
.Java configuration for repositories
====
[source,java]
----
@Configuration
@EnableR2dbcRepositories
class ApplicationConfig extends AbstractR2dbcConfiguration {
@Override
public ConnectionFactory connectionFactory() {
return …;
}
}
----
====
Because our domain repository extends `ReactiveCrudRepository`, it provides you with CRUD operations to access the entities.
Working with the repository instance is just a matter of dependency injecting it into a client.
Consequently, you can retrieve all `Person` objects would resemble the following code:
.Paging access to Person entities
====
[source,java]
----
@RunWith(SpringRunner.class)
@ContextConfiguration
public class PersonRepositoryTests {
@Autowired PersonRepository repository;
@Test
public void readsallEntitiesCorrectly() {
repository.findAll()
.as(StepVerifier::create)
.expectNextCount(1)
.verifyComplete();
}
}
----
====
The preceding example creates an application context with Spring's unit test support, which performs annotation-based dependency injection into test cases.
Inside the test method, we use the repository to query the database.
We use `StepVerifier` as test aid to verify our expectations against the results.
[[r2dbc.repositories.queries]]
== Query Methods
Most of the data access operations you usually trigger on a repository result in a query being executed against the databases.
Defining such a query is a matter of declaring a method on the repository interface, as the following example shows:
.PersonRepository with query methods
====
[source,java]
----
public interface PersonRepository extends ReactiveCrudRepository<Person, Long> {
@Query("SELECT * FROM person WHERE lastname = $1")
Flux<Person> findByLastname(String lastname); <1>
@Query("SELECT firstname, lastname FROM person WHERE lastname = $1")
Mono<Person> findFirstByLastname(String lastname) <2>
}
----
<1> The `findByLastname` method shows a query for all people with the given last name.
The query is provided as R2DBC repositories do not support query derivation.
The annotated query uses native bind markers, which are Postgres bind markers in this example.
<4> A query for a single `Person` entity projecting only `firstname` and `lastname` columns.
====
NOTE: R2DBC repositories do not support query derivation.

View File

@@ -0,0 +1,493 @@
[[r2dbc.core]]
= R2DBC support
The R2DBC support contains a wide range of features:
* Spring configuration support with Java-based `@Configuration` classes for an R2DBC driver instance.
* `DatabaseClient` helper class that increases productivity when performing common R2DBC operations with integrated object mapping between rows and POJOs.
* Exception translation into Spring's portable Data Access Exception hierarchy.
* Feature-rich Object Mapping integrated with Spring's Conversion Service.
* Annotation-based mapping metadata that is extensible to support other metadata formats.
* Automatic implementation of Repository interfaces, including support for custom query methods.
For most tasks, you should use `DatabaseClient` or the Repository support, which both leverage the rich mapping functionality.
`DatabaseClient` is the place to look for accessing functionality such as ad-hoc CRUD operations.
[[r2dbc.getting-started]]
== Getting Started
An easy way to bootstrap setting up a working environment is to create a Spring-based project through https://start.spring.io[start.spring.io].
.Add the following to the pom.xml files `dependencies` element:
+
[source,xml,subs="+attributes"]
----
<dependencies>
<!-- other dependency elements omitted -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-r2dbc</artifactId>
<version>{version}</version>
</dependency>
<!-- a R2DBC driver -->
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>…</artifactId>
<version>{r2dbcVersion}</version>
</dependency>
</dependencies>
----
. Change the version of Spring in the pom.xml to be
+
[source,xml,subs="+attributes"]
----
<spring.framework.version>{springVersion}</spring.framework.version>
----
. Add the following location of the Spring Milestone repository for Maven to your `pom.xml` such that it is at the same level of your `<dependencies/>` element:
+
[source,xml]
----
<repositories>
<repository>
<id>spring-milestone</id>
<name>Spring Maven MILESTONE Repository</name>
<url>http://repo.spring.io/libs-milestone</url>
</repository>
</repositories>
----
The repository is also http://repo.spring.io/milestone/org/springframework/data/[browseable here].
You may also want to set the logging level to `DEBUG` to see some additional information. To do so, edit the `application.properties` file to have the following content:
[source]
----
logging.level.org.springframework.data.r2dbc=DEBUG
----
Then you can create a `Person` class to persist:
[source,java]
----
package org.spring.r2dbc.example;
public class Person {
private String id;
private String name;
private int age;
public Person(String id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", age=" + age + "]";
}
}
----
Next, you need to create a table structure in your database:
[source,sql]
----
CREATE TABLE person
(id VARCHAR(255) PRIMARY KEY,
name VARCHAR(255),
age INT);
----
You also need a main application to run:
[source,java]
----
package org.spring.r2dbc.example;
public class R2dbcApp {
private static final Log log = LogFactory.getLog(R2dbcApp.class);
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new H2ConnectionFactory(H2ConnectionConfiguration.builder()
.url("mem:test;DB_CLOSE_DELAY=10")
.build());
DatabaseClient client = DatabaseClient.create(connectionFactory);
client.execute()
.sql("CREATE TABLE person" +
" (id VARCHAR(255) PRIMARY KEY," +
" name VARCHAR(255)," +
" age INT)")
.fetch()
.rowsUpdated()
.as(StepVerifier::create)
.expectNextCount(1)
.verifyComplete();
client.insert()
.into(Person.class)
.using(new Person("joe", "Joe", 34))
.then()
.as(StepVerifier::create)
.verifyComplete();
client.select()
.from(Person.class)
.fetch()
.first()
.doOnNext(it -> log.info(it))
.as(StepVerifier::create)
.expectNextCount(1)
.verifyComplete();
}
}
----
When you run the main program, the preceding examples produce the following output:
[source]
----
2018-11-28 10:47:03,893 DEBUG ata.r2dbc.function.DefaultDatabaseClient: 310 - Executing SQL statement [CREATE TABLE person
(id VARCHAR(255) PRIMARY KEY,
name VARCHAR(255),
age INT)]
2018-11-28 10:47:04,074 DEBUG ata.r2dbc.function.DefaultDatabaseClient: 908 - Executing SQL statement [INSERT INTO person (id, name, age) VALUES($1, $2, $3)]
2018-11-28 10:47:04,092 DEBUG ata.r2dbc.function.DefaultDatabaseClient: 575 - Executing SQL statement [SELECT id, name, age FROM person]
2018-11-28 10:47:04,436 INFO org.spring.r2dbc.example.R2dbcApp: 43 - Person [id='joe', name='Joe', age=34]
----
Even in this simple example, there are few things to notice:
* You can create an instance of the central helper class in Spring Data R2DBC, <<r2dbc.datbaseclient,`DatabaseClient`>>, by using a standard `io.r2dbc.spi.ConnectionFactory` object.
* The mapper works against standard POJO objects without the need for any additional metadata (though you can optionally provide that information. See <<mapping-chapter,here>>.).
* Mapping conventions can use field access. Notice that the `Person` class has only getters.
* If the constructor argument names match the column names of the stored row, they are used to instantiate the object.
[[r2dbc.examples-repo]]
== Examples Repository
There is a https://github.com/spring-projects/spring-data-examples[GitHub repository with several examples] that you can download and play around with to get a feel for how the library works.
[[r2dbc.drivers]]
== Connecting to a Relational Database with Spring
One of the first tasks when using relational databases and Spring is to create a `io.r2dbc.spi.ConnectionFactory` object using the IoC container. The following example explains Java-based configuration.
[[r2dbc.connectionfactory]]
=== Registering a `ConnectionFactory` Instance using Java-based Metadata
The following example shows an example of using Java-based bean metadata to register an instance of a `io.r2dbc.spi.ConnectionFactory`:
.Registering a `io.r2dbc.spi.ConnectionFactory` object using Java-based bean metadata
====
[source,java]
----
@Configuration
public class ApplicationConfiguration extends AbstractR2dbcConfiguration {
@Override
@Bean
public ConnectionFactory connectionFactory() {
return …;
}
}
----
====
This approach lets you use the standard `io.r2dbc.spi.ConnectionFactory` instance, with the container using Spring's `AbstractR2dbcConfiguration`. As compared to registering a `ConnectionFactory` instance directly, the configuration support has the added advantage of also providing the container with an `ExceptionTranslator` implementation that translates R2DBC exceptions to exceptions in Spring's portable `DataAccessException` hierarchy for data access classes annotated with the `@Repository` annotation. This hierarchy and the use of `@Repository` is described in http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/data-access.html[Spring's DAO support features].
`AbstractR2dbcConfiguration` registers also `DatabaseClient` that is required for database interaction and for Repository implementation.
[[r2dbc.drivers]]
=== R2DBC Drivers
Spring Data R2DBC supports drivers by R2DBC's pluggable SPI mechanism. Any driver implementing the R2DBC spec can be used with Spring Data R2DBC.
R2DBC is a relatively young initiative that gains significance by maturing through adoption.
As of writing the following 3 drivers are available:
* https://github.com/r2dbc/r2dbc-postgresql[Postgres] (`io.r2dbc:r2dbc-postgresql`)
* https://github.com/r2dbc/r2dbc-h2[H2] (`io.r2dbc:r2dbc-h2`)
* https://github.com/r2dbc/r2dbc-mssql[Microsoft SQL Server] (`io.r2dbc:r2dbc-mssql`)
Spring Data R2DBC reacts to database specifics by inspecting `ConnectionFactoryMetadata` and selects the appropriate database dialect.
You can configure an own `Dialect` if the used driver is not yet known to Spring Data R2DBC.
[[r2dbc.datbaseclient]]
== Introduction to `DatabaseClient`
Spring Data R2DBC includes a reactive, non-blocking `DatabaseClient` for database interaction. The client has a functional, fluent API with reactive types for declarative composition.
`DatabaseClient` encapsulates resource handling such as opening and closing connections so your application code can make use of executing SQL queries or calling higher-level functionality such as inserting or selecting data.
NOTE: `DatabaseClient` is a young application component providing a minimal set of convenience methods that is likely to be extended through time.
NOTE: Once configured, `DatabaseClient` is thread-safe and can be reused across multiple instances.
Another central feature of `DatabaseClient` is translation of exceptions thrown by R2DBC drivers into Spring's portable Data Access Exception hierarchy. See "`<<r2dbc.exception>>`" for more information.
The next section contains an example of how to work with the `DatabaseClient` in the context of the Spring container.
[[r2dbc.datbaseclient.create]]
=== Creating `DatabaseClient`
The simplest way to create a `DatabaseClient` is through a static factory method:
[source,java]
----
DatabaseClient.create(ConnectionFactory connectionFactory)
----
The above method creates a `DatabaseClient` with default settings.
You can also use `DatabaseClient.builder()` with further options to customize the client:
* `exceptionTranslator`: Supply a specific `R2dbcExceptionTranslator` to customize how R2DBC exceptions are translated into Spring's portable Data Access Exception hierarchy. See "`<<r2dbc.exception>>`" for more information.
* `dataAccessStrategy`: Strategy how SQL queries are generated and how objects are mapped.
Once built, a `DatabaseClient` instance is immutable. However, you can clone it and build a modified copy without affecting the original instance, as the following example shows:
[source,java]
----
DatabaseClient client1 = DatabaseClient.builder()
.exceptionTranslator(exceptionTranslatorA).build();
DatabaseClient client2 = client1.mutate()
.exceptionTranslator(exceptionTranslatorB).build();
----
=== Controlling Database Connections
Spring Data R2DBC obtains a connection to the database through a `ConnectionFactory`.
A `ConnectionFactory` is part of the R2DBC specification and is a generalized connection factory.
It lets a container or a framework hide connection pooling and transaction management issues from the application code.
When you use Spring Data R2DBC, you can create a `ConnectionFactory` using your R2DBC driver.
`ConnectionFactory` implementations can either return the same connection, different connections or provide connection pooling.
`DatabaseClient` uses `ConnectionFactory` to create and release connections per operation without affinity to a particular connection across multiple operations.
[[r2dbc.exception]]
== Exception Translation
The Spring framework provides exception translation for a wide variety of database and mapping technologies.
This has traditionally been for JDBC and JPA. The Spring support for R2DBC extends this feature by providing implementations of the `R2dbcExceptionTranslator` interface.
`R2dbcExceptionTranslator` is an interface to be implemented by classes that can translate between `R2dbcException` and Springs own `org.springframework.dao.DataAccessException`, which is agnostic in regard to data access strategy.
Implementations can be generic (for example, using SQLState codes) or proprietary (for example, using Postgres error codes) for greater precision.
`SqlErrorCodeR2dbcExceptionTranslator` is the implementation of `R2dbcExceptionTranslator` that is used by default.
This implementation uses specific vendor codes.
It is more precise than the SQLState implementation.
The error code translations are based on codes held in a JavaBean type class called `SQLErrorCodes`.
This class is created and populated by an `SQLErrorCodesFactory`, which (as the name suggests) is a factory for creating SQLErrorCodes based on the contents of a configuration file named `sql-error-codes.xml` from Spring's Data Access module.
This file is populated with vendor codes and based on the `ConnectionFactoryName` taken from `ConnectionFactoryMetadata`.
The codes for the actual database you are using are used.
The `SqlErrorCodeR2dbcExceptionTranslator` applies matching rules in the following sequence:
1. Any custom translation implemented by a subclass. Normally, the provided concrete `SqlErrorCodeR2dbcExceptionTranslator` is used, so this rule does not apply. It applies only if you have actually provided a subclass implementation.
2. Any custom implementation of the `SQLExceptionTranslator` interface that is provided as the `customSqlExceptionTranslator` property of the `SQLErrorCodes` class.
3. Error code matching is applied.
4. Use a fallback translator.
NOTE: The `SQLErrorCodesFactory` is used by default to define Error codes and custom exception translations. They are looked up in a file named `sql-error-codes.xml` from the classpath, and the matching `SQLErrorCodes` instance is located based on the database name from the database metadata of the database in use. `SQLErrorCodesFactory` is as of now part of Spring JDBC. Spring Data R2DBC reuses existing translation configurations.
You can extend `SqlErrorCodeR2dbcExceptionTranslator`, as the following example shows:
[source,java]
----
public class CustomSqlErrorCodeR2dbcExceptionTranslator extends SqlErrorCodeR2dbcExceptionTranslator {
protected DataAccessException customTranslate(String task, String sql, R2dbcException r2dbcex) {
if (sqlex.getErrorCode() == -12345) {
return new DeadlockLoserDataAccessException(task, r2dbcex);
}
return null;
}
}
----
In the preceding example, the specific error code (`-12345`) is translated, while other errors are left to be translated by the default translator implementation.
To use this custom translator, you must configure `DatabaseClient` through the builder method `exceptionTranslator`, and you must use this `DatabaseClient` for all of the data access processing where this translator is needed.
The following example shows how you can use this custom translator:
[source,java]
----
ConnectionFactory connectionFactory = …;
CustomSqlErrorCodeR2dbcExceptionTranslator exceptionTranslator = new CustomSqlErrorCodeR2dbcExceptionTranslator();
DatabaseClient client = DatabaseClient.builder()
.connectionFactory(connectionFactory)
.exceptionTranslator(exceptionTranslator)
.build();
----
[[r2dbc.datbaseclient.statements]]
=== Running Statements
Running a statement is the basic functionality that is covered by `DatabaseClient`.
The following example shows what you need to include for a minimal but fully functional class that creates a new table:
[source,java]
----
Mono<Void> completion = client.execute()
.sql("CREATE TABLE person (id VARCHAR(255) PRIMARY KEY, name VARCHAR(255), age INTEGER);")
.then();
----
`DatabaseClient` is designed for a convenient fluent usage.
It exposes intermediate, continuation, and terminal methods at each stage of the execution specification.
The example above uses `then()` to return a completion `Publisher` that completes as soon as the query (or queries, if the SQL query contains multiple statements) completes.
NOTE: `execute().sql(…)` accepts either the SQL query string or a query `Supplier<String>` to defer the actual query creation until execution.
[[r2dbc.datbaseclient.queries]]
=== Running Queries
SQL queries can return values or the number of affected rows.
`DatabaseClient` can return the number of updated rows or the rows themself, depending on the issued query.
The following example shows an `UPDATE` statement that returns the number of updated rows:
[source,java]
----
Mono<Integer> affectedRows = client.execute()
.sql("UPDATE person SET name = 'Joe'")
.fetch().rowsUpdated();
----
Running a `SELECT` query returns a different type of result, in particular tabular results. Tabular data is typically consumes by streaming each `Row`.
You might have noticed the use of `fetch()` in the previous example.
`fetch()` is a continuation operator that allows you to specify how much data you want to consume.
[source,java]
----
Mono<Map<String, Object>> first = client.execute()
.sql("SELECT id, name FROM person")
.fetch().first();
----
Calling `first()` returns the first row from the result and discards remaining rows.
You can consume data with the following operators:
* `first()` return the first row of the entire result
* `one()` returns exactly one result and fails if the result contains more rows.
* `all()` returns all rows of the result
* `rowsUpdated()` returns the number of affected rows (`INSERT` count, `UPDATE` count)
`DatabaseClient` queries return their results by default as `Map` of column name to value. You can customize type mapping by applying an `as(Class<T>)` operator.
[source,java]
----
Flux<Person> all = client.execute()
.sql("SELECT id, name FROM mytable")
.as(Person.class)
.fetch().all();
----
`as(…)` applies <<mapping-conventions,Convention-based Object Mapping>> and maps the resulting columns to your POJO.
[[r2dbc.datbaseclient.mapping]]
=== Mapping Results
You can customize result extraction beyong `Map` and POJO result extraction by providing an extractor `BiFunction<Row, RowMetadata, T>`.
The extractor function interacts directly with R2DBC's `Row` and `RowMetadata` objects and can return arbitrary values (singular values, collections/maps, objects).
The following example extracts the `id` column and emits its value:
[source,java]
----
Flux<String> names= client.execute()
.sql("SELECT name FROM person")
.fetch()
.extract((row, rowMetadata) -> row.get("id", String.class))
.all();
----
[[r2dbc.datbaseclient.mapping.null]]
.What about `null`?
****
Relational database results may contain `null` values.
Reactive Streams forbids emission of `null` values which requires a proper `null` handling in the extractor function.
While you can obtain `null` values from a `Row`, you must not emit a `null` value.
You must wrap any `null` values in an object (e.g. `Optional` for singular values) to make sure a `null` value is never returned directly by your extractor function.
****
[[r2dbc.datbaseclient.binding]]
=== Binding Values to Queries
A typical application requires parametrized SQL statements to select or update rows according to some input.
These are typically `SELECT` statements constrained by a `WHERE` clause or `INSERT`/`UPDATE` statements accepting input parameters.
Parametrized statements bear the risk of SQL injection if parameters are not escaped properly.
`DatabaseClient` leverages R2DBC's Bind API to eliminate the risk of SQL injection for query parameters.
You can provide a parametrized SQL statement with the `sql(…)` operator and bind parameters to the actual `Statement`.
Your R2DBC driver then executes the statement using prepared statements and parameter substitution.
Parameter binding supports various binding strategies:
* By Index using zero-based parameter indexes.
* By Name using the placeholder name.
The following example shows parameter binding for a PostgreSQL query:
[source,java]
----
db.execute()
.sql("INSERT INTO person (id, name, age) VALUES($1, $2, $3)")
.bind(0, "joe")
.bind(1, "Joe")
.bind(2, 34);
----
NOTE: If you are familiar with JDBC, then you're also familiar with `?` (questionmark) bind markers.
JDBC drivers translate questionmark bindmarkers to database-native markers as part of statement execution.
Make sure to use the appropriate bind markers that are supported by your database as R2DBC requires database-native parameter bind markers.
[[r2dbc.datbaseclient.transactions]]
=== Transactions
A common pattern when using relational databases is grouping multiple queries within a unit of work that is guarded by a transaction.
Relational databases typically associate a transaction with a single transport connection.
Using different connections hence results in utilizing different transactions.
Spring Data R2DBC includes a transactional `DatabaseClient` implementation with `TransactionalDatabaseClient` that allows you to group multiple statements within the same transaction.
`TransactionalDatabaseClient` is a extension of `DatabaseClient` that exposes the same functionality as `DatabaseClient` and adds transaction-management methods.
You can run multiple statements within a transaction using the `inTransaction(Function)` closure:
[source,java]
----
TransactionalDatabaseClient databaseClient = TransactionalDatabaseClient.create(connectionFactory);
Flux<Void> completion = databaseClient.inTransaction(db -> {
return db.execute().sql("INSERT INTO person (id, name, age) VALUES($1, $2, $3)") //
.bind(0, "joe") //
.bind(1, "Joe") //
.bind(2, 34) //
.fetch().rowsUpdated()
.then(db.execute().sql("INSERT INTO contacts (id, name) VALUES($1, $2)")
.bind(0, "joe")
.bind(1, "Joe")
.fetch().rowsUpdated())
.then();
});
----

View File

@@ -0,0 +1,3 @@
Spring Data R2DBC Changelog
===========================

View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,10 @@
Spring Data R2DBC 1.0.0.BUILD-SNAPSHOT
Copyright (c) [2018] Pivotal Software, Inc.
This product is licensed to you under the Apache License, Version 2.0 (the "License").
You may not use this product except in compliance with the License.
This product may include a number of subcomponents with
separate copyright notices and license terms. Your use of the source
code for the these subcomponents is subject to the terms and
conditions of the subcomponent's license, as noted in the LICENSE file.