|
| 1 | +The R2DBC support contains a wide range of features: |
| 2 | + |
| 3 | +* Spring configuration support with Java-based `@Configuration` classes for an R2DBC driver instance. |
| 4 | +* `DatabaseClient` helper class that increases productivity when performing common R2DBC operations with integrated object mapping between rows and POJOs. |
| 5 | +* Exception translation into Spring's portable Data Access Exception hierarchy. |
| 6 | +* Feature-rich Object Mapping integrated with Spring's Conversion Service. |
| 7 | +* Annotation-based mapping metadata that is extensible to support other metadata formats. |
| 8 | +* Automatic implementation of Repository interfaces, including support for custom query methods. |
| 9 | +
|
| 10 | +For most tasks, you should use `DatabaseClient` or the Repository support, which both leverage the rich mapping functionality. |
| 11 | +`DatabaseClient` is the place to look for accessing functionality such as ad-hoc CRUD operations. |
| 12 | + |
| 13 | +[[r2dbc.getting-started]] |
| 14 | +== Getting Started |
| 15 | + |
| 16 | +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]. |
| 17 | + |
| 18 | +. Add the following to the pom.xml files `dependencies` element: |
| 19 | ++ |
| 20 | +[source,xml,subs="+attributes"] |
| 21 | +---- |
| 22 | +<dependencyManagement> |
| 23 | + <dependencies> |
| 24 | + <dependency> |
| 25 | + <groupId>io.r2dbc</groupId> |
| 26 | + <artifactId>r2dbc-bom</artifactId> |
| 27 | + <version>${r2dbc-releasetrain.version}</version> |
| 28 | + <type>pom</type> |
| 29 | + <scope>import</scope> |
| 30 | + </dependency> |
| 31 | + </dependencies> |
| 32 | +</dependencyManagement> |
| 33 | +
|
| 34 | +<dependencies> |
| 35 | +
|
| 36 | + <!-- other dependency elements omitted --> |
| 37 | +
|
| 38 | + <dependency> |
| 39 | + <groupId>org.springframework.data</groupId> |
| 40 | + <artifactId>spring-data-r2dbc</artifactId> |
| 41 | + <version>{version}</version> |
| 42 | + </dependency> |
| 43 | +
|
| 44 | + <!-- a R2DBC driver --> |
| 45 | + <dependency> |
| 46 | + <groupId>io.r2dbc</groupId> |
| 47 | + <artifactId>r2dbc-h2</artifactId> |
| 48 | + <version>{r2dbcVersion}</version> |
| 49 | + </dependency> |
| 50 | +
|
| 51 | +</dependencies> |
| 52 | +---- |
| 53 | +. Change the version of Spring in the pom.xml to be |
| 54 | ++ |
| 55 | +[source,xml,subs="+attributes"] |
| 56 | +---- |
| 57 | +<spring.framework.version>{springVersion}</spring.framework.version> |
| 58 | +---- |
| 59 | +. 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: |
| 60 | ++ |
| 61 | +[source,xml] |
| 62 | +---- |
| 63 | +<repositories> |
| 64 | + <repository> |
| 65 | + <id>spring-milestone</id> |
| 66 | + <name>Spring Maven MILESTONE Repository</name> |
| 67 | + <url>https://repo.spring.io/libs-milestone</url> |
| 68 | + </repository> |
| 69 | +</repositories> |
| 70 | +---- |
| 71 | + |
| 72 | +The repository is also https://repo.spring.io/milestone/org/springframework/data/[browseable here]. |
| 73 | + |
| 74 | +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: |
| 75 | + |
| 76 | +[source] |
| 77 | +---- |
| 78 | +logging.level.org.springframework.data.r2dbc=DEBUG |
| 79 | +---- |
| 80 | + |
| 81 | +Then you can create a `Person` class to persist: |
| 82 | + |
| 83 | +[source,java] |
| 84 | +---- |
| 85 | +package org.spring.r2dbc.example; |
| 86 | +
|
| 87 | +public class Person { |
| 88 | +
|
| 89 | + private String id; |
| 90 | + private String name; |
| 91 | + private int age; |
| 92 | +
|
| 93 | + public Person(String id, String name, int age) { |
| 94 | + this.id = id; |
| 95 | + this.name = name; |
| 96 | + this.age = age; |
| 97 | + } |
| 98 | +
|
| 99 | + public String getId() { |
| 100 | + return id; |
| 101 | + } |
| 102 | + public String getName() { |
| 103 | + return name; |
| 104 | + } |
| 105 | + public int getAge() { |
| 106 | + return age; |
| 107 | + } |
| 108 | +
|
| 109 | + @Override |
| 110 | + public String toString() { |
| 111 | + return "Person [id=" + id + ", name=" + name + ", age=" + age + "]"; |
| 112 | + } |
| 113 | +} |
| 114 | +---- |
| 115 | + |
| 116 | +Next, you need to create a table structure in your database: |
| 117 | + |
| 118 | +[source,sql] |
| 119 | +---- |
| 120 | +CREATE TABLE person |
| 121 | + (id VARCHAR(255) PRIMARY KEY, |
| 122 | + name VARCHAR(255), |
| 123 | + age INT); |
| 124 | +---- |
| 125 | + |
| 126 | +You also need a main application to run: |
| 127 | + |
| 128 | +[source,java] |
| 129 | +---- |
| 130 | +package org.spring.r2dbc.example; |
| 131 | +
|
| 132 | +public class R2dbcApp { |
| 133 | +
|
| 134 | + private static final Log log = LogFactory.getLog(R2dbcApp.class); |
| 135 | +
|
| 136 | + public static void main(String[] args) throws Exception { |
| 137 | +
|
| 138 | + ConnectionFactory connectionFactory = ConnectionFactories.get("rdbc:h2:mem:///test?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE"); |
| 139 | +
|
| 140 | + DatabaseClient client = DatabaseClient.create(connectionFactory); |
| 141 | +
|
| 142 | + client.execute() |
| 143 | + .sql("CREATE TABLE person" + |
| 144 | + "(id VARCHAR(255) PRIMARY KEY," + |
| 145 | + "name VARCHAR(255)," + |
| 146 | + "age INT)") |
| 147 | + .fetch() |
| 148 | + .rowsUpdated() |
| 149 | + .as(StepVerifier::create) |
| 150 | + .expectNextCount(1) |
| 151 | + .verifyComplete(); |
| 152 | +
|
| 153 | + client.insert() |
| 154 | + .into(Person.class) |
| 155 | + .using(new Person("joe", "Joe", 34)) |
| 156 | + .then() |
| 157 | + .as(StepVerifier::create) |
| 158 | + .verifyComplete(); |
| 159 | +
|
| 160 | + client.select() |
| 161 | + .from(Person.class) |
| 162 | + .fetch() |
| 163 | + .first() |
| 164 | + .doOnNext(it -> log.info(it)) |
| 165 | + .as(StepVerifier::create) |
| 166 | + .expectNextCount(1) |
| 167 | + .verifyComplete(); |
| 168 | + } |
| 169 | +} |
| 170 | +---- |
| 171 | + |
| 172 | +When you run the main program, the preceding examples produce output similar to the following: |
| 173 | + |
| 174 | +[source] |
| 175 | +---- |
| 176 | +2018-11-28 10:47:03,893 DEBUG ata.r2dbc.function.DefaultDatabaseClient: 310 - Executing SQL statement [CREATE TABLE person |
| 177 | + (id VARCHAR(255) PRIMARY KEY, |
| 178 | + name VARCHAR(255), |
| 179 | + age INT)] |
| 180 | +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)] |
| 181 | +2018-11-28 10:47:04,092 DEBUG ata.r2dbc.function.DefaultDatabaseClient: 575 - Executing SQL statement [SELECT id, name, age FROM person] |
| 182 | +2018-11-28 10:47:04,436 INFO org.spring.r2dbc.example.R2dbcApp: 43 - Person [id='joe', name='Joe', age=34] |
| 183 | +---- |
| 184 | + |
| 185 | +Even in this simple example, there are few things to notice: |
| 186 | + |
| 187 | +* 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. |
| 188 | +* 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>>.). |
| 189 | +* Mapping conventions can use field access. Notice that the `Person` class has only getters. |
| 190 | +* If the constructor argument names match the column names of the stored row, they are used to instantiate the object. |
| 191 | + |
| 192 | +[[r2dbc.examples-repo]] |
| 193 | +== Examples Repository |
| 194 | + |
| 195 | +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. |
| 196 | + |
| 197 | +[[r2dbc.drivers]] |
| 198 | +== Connecting to a Relational Database with Spring |
| 199 | + |
| 200 | +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. |
| 201 | + |
| 202 | +[[r2dbc.connectionfactory]] |
| 203 | +=== Registering a `ConnectionFactory` Instance using Java-based Metadata |
| 204 | + |
| 205 | +The following example shows an example of using Java-based bean metadata to register an instance of a `io.r2dbc.spi.ConnectionFactory`: |
| 206 | + |
| 207 | +.Registering a `io.r2dbc.spi.ConnectionFactory` object using Java-based bean metadata |
| 208 | +==== |
| 209 | +[source,java] |
| 210 | +---- |
| 211 | +@Configuration |
| 212 | +public class ApplicationConfiguration extends AbstractR2dbcConfiguration { |
| 213 | +
|
| 214 | + @Override |
| 215 | + @Bean |
| 216 | + public ConnectionFactory connectionFactory() { |
| 217 | + return …; |
| 218 | + } |
| 219 | +} |
| 220 | +---- |
| 221 | +==== |
| 222 | + |
| 223 | +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 https://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/data-access.html[Spring's DAO support features]. |
| 224 | + |
| 225 | +`AbstractR2dbcConfiguration` registers also `DatabaseClient` that is required for database interaction and for Repository implementation. |
| 226 | + |
| 227 | +[[r2dbc.drivers]] |
| 228 | +=== R2DBC Drivers |
| 229 | + |
| 230 | +Spring Data R2DBC supports drivers by R2DBC's pluggable SPI mechanism. Any driver implementing the R2DBC spec can be used with Spring Data R2DBC. |
| 231 | +R2DBC is a relatively young initiative that gains significance by maturing through adoption. |
| 232 | +As of writing the following 3 drivers are available: |
| 233 | + |
| 234 | +* https://github.com/r2dbc/r2dbc-postgresql[Postgres] (`io.r2dbc:r2dbc-postgresql`) |
| 235 | +* https://github.com/r2dbc/r2dbc-h2[H2] (`io.r2dbc:r2dbc-h2`) |
| 236 | +* https://github.com/r2dbc/r2dbc-mssql[Microsoft SQL Server] (`io.r2dbc:r2dbc-mssql`) |
| 237 | +* https://github.com/jasync-sql/jasync-sql[Microsoft SQL Server] (`com.github.jasync-sql:jasync-r2dbc-mysql`) |
| 238 | + |
| 239 | +Spring Data R2DBC reacts to database specifics by inspecting `ConnectionFactoryMetadata` and selects the appropriate database dialect. |
| 240 | +You can configure an own `Dialect` if the used driver is not yet known to Spring Data R2DBC. |
| 241 | + |
0 commit comments