|
| 1 | +[[r2dbc.datbaseclient.fluent-api]] |
| 2 | += Fluent Data Access API |
| 3 | + |
| 4 | +You have already seen ``DatabaseClient``s SQL API that offers you maximum flexibility to execute any type of SQL. |
| 5 | +`DatabaseClient` provides a more narrow interface for typical ad-hoc use-cases such as querying, inserting, updating, and deleting data. |
| 6 | + |
| 7 | +The entry points (`insert()`, `select()`, `update()`, and others) follow a natural naming schema based on the operation to be run. Moving on from the entry point, the API is designed to offer only context-dependent methods that lead to a terminating method that creates and runs a SQL statement. Spring Data R2DBC uses a `Dialect` abstraction to determine bind markers, pagination support and data types natively supported by the underlying driver. |
| 8 | + |
| 9 | +Let's take a look at a simple query: |
| 10 | + |
| 11 | +==== |
| 12 | +[source,java] |
| 13 | +---- |
| 14 | +Flux<Person> people = databaseClient.select() |
| 15 | + .from(Person.class) <1> |
| 16 | + .fetch() |
| 17 | + .all(); <2> |
| 18 | +---- |
| 19 | +<1> Using `Person` with the `from(…)` method sets the `FROM` table based on mapping metadata. It also maps tabular results on `Person` result objects. |
| 20 | +<2> Fetching `all()` rows returns a `Flux<Person>` without limiting results. |
| 21 | +==== |
| 22 | + |
| 23 | +The following example declares a more complex query that specifies the table name by name, a `WHERE` condition and `ORDER BY` clause: |
| 24 | + |
| 25 | +==== |
| 26 | +[source,java] |
| 27 | +---- |
| 28 | +Mono<Person> first = databaseClient.select() |
| 29 | + .from("legoset") <1> |
| 30 | + .matching(where("firstname").is("John") <2> |
| 31 | + .and("lastname").in("Doe", "White")) |
| 32 | + .orderBy(desc("id")) <3> |
| 33 | + .as(Person.class) |
| 34 | + .fetch() |
| 35 | + .one(); <4> |
| 36 | +---- |
| 37 | +<1> Selecting from a table by name returns row results as `Map<String, Object>` with case-insensitive column name matching. |
| 38 | +<2> The issued query declares a `WHERE` condition on `firstname` and `lastname` columns to filter results. |
| 39 | +<3> Results can be ordered by individual column names resulting in an `ORDER BY` clause. |
| 40 | +<4> Selecting the one result fetches just a single row. This way of consuming rows expects the query to return exactly a single result. `Mono` emits a `IncorrectResultSizeDataAccessException` if the query yields more than a single result. |
| 41 | +==== |
| 42 | + |
| 43 | +You can consume Query results in three ways: |
| 44 | + |
| 45 | +* Through object mapping (e.g. `as(Class<T>)`) using Spring Data's mapping-metadata. |
| 46 | +* As `Map<String, Object>` where column names are mapped to their value. Column names are looked up case-insensitive. |
| 47 | +* By supplying a mapping `BiFunction` for direct access to R2DBC `Row` and `RowMetadata` |
| 48 | + |
| 49 | +You can switch between retrieving a single entity and retrieving multiple entities as through the terminating methods: |
| 50 | + |
| 51 | +* `first()`: Consume only the first row returning a `Mono`. The returned `Mono` completes without emitting an object if the query returns no results. |
| 52 | +* `one()`: Consume exactly one row returning a `Mono`. The returned `Mono` completes without emitting an object if the query returns no results. If the query returns more than row then `Mono` completes exceptionally emitting `IncorrectResultSizeDataAccessException`. |
| 53 | +* `all()`: Consume all returned rows returning a `Flux`. |
| 54 | +* `rowsUpdated`: Consume the number of affected rows. Typically used with `INSERT`/`UPDATE`/`DELETE` statements. |
| 55 | + |
| 56 | +[[r2dbc.datbaseclient.fluent-api.select]] |
| 57 | +== Selecting Data |
| 58 | + |
| 59 | +Use the `select()` entry point to express your `SELECT` queries. |
| 60 | +The resulting `SELECT` queries support the commonly used clauses `WHERE`, `ORDER BY` and support pagination. |
| 61 | +The fluent API style allows you to chain together multiple methods while having easy-to-understand code. |
| 62 | +To improve readability, use static imports that allow you avoid using the 'new' keyword for creating `Criteria` instances. |
| 63 | + |
| 64 | +[r2dbc.datbaseclient.fluent-api.criteria]] |
| 65 | +==== Methods for the Criteria Class |
| 66 | + |
| 67 | +The `Criteria` class provides the following methods, all of which correspond to SQL operators: |
| 68 | + |
| 69 | +* `Criteria` *and* `(String column)` Adds a chained `Criteria` with the specified `property` to the current `Criteria` and returns the newly created one. |
| 70 | +* `Criteria` *or* `(String column)` Adds a chained `Criteria` with the specified `property` to the current `Criteria` and returns the newly created one. |
| 71 | +* `Criteria` *greaterThan* `(Object o)` Creates a criterion using the `>` operator. |
| 72 | +* `Criteria` *greaterThanOrEquals* `(Object o)` Creates a criterion using the `>=` operator. |
| 73 | +* `Criteria` *in* `(Object... o)` Creates a criterion using the `IN` operator for a varargs argument. |
| 74 | +* `Criteria` *in* `(Collection<?> collection)` Creates a criterion using the `IN` operator using a collection. |
| 75 | +* `Criteria` *is* `(Object o)` Creates a criterion using column matching (`property = value`). |
| 76 | +* `Criteria` *isNull* `()` Creates a criterion using the `IS NULL` operator. |
| 77 | +* `Criteria` *isNotNull* `()` Creates a criterion using the `IS NOT NULL` operator. |
| 78 | +* `Criteria` *lessThan* `(Object o)` Creates a criterion using the `<` operator. |
| 79 | +* `Criteria` *lessThanOrEquals* `(Object o)` Creates a criterion using the `<=` operator. |
| 80 | +* `Criteria` *like* `(Object o)` Creates a criterion using the `LIKE` operator without escape character processing. |
| 81 | +* `Criteria` *not* `(Object o)` Creates a criterion using the `!=` operator. |
| 82 | +* `Criteria` *notIn* `(Object... o)` Creates a criterion using the `NOT IN` operator for a varargs argument. |
| 83 | +* `Criteria` *notIn* `(Collection<?> collection)` Creates a criterion using the `NOT IN` operator using a collection. |
| 84 | + |
| 85 | +You can use `Criteria` with `SELECT`, `UPDATE`, and `DELETE` queries. |
| 86 | + |
| 87 | +[r2dbc.datbaseclient.fluent-api.select.methods]] |
| 88 | +==== Methods for SELECT operations |
| 89 | + |
| 90 | +The `select()` entry point exposes some additional methods that provide options for the query: |
| 91 | + |
| 92 | +* *from* `(Class<T>)` used to specify the source table using a mapped object. Returns results by default as `T`. |
| 93 | +* *from* `(String)` used to specify the source table name. Returns results by default as `Map<String, Object>`. |
| 94 | +* *as* `(Class<T>)` used to map results to `T`. |
| 95 | +* *map* `(BiFunction<Row, RowMetadata, T>)` used to supply a mapping function to extract results. |
| 96 | +* *project* `(String... columns)` used to specify which columns to return. |
| 97 | +* *matching* `(Criteria)` used to declare a `WHERE` condition to filter results. |
| 98 | +* *orderBy* `(Order)` used to declare a `ORDER BY` clause to sort results. |
| 99 | +* *page* `(Page pageable)` used to retrieve a particular page within the result. Limits the size of the returned results and reads from a offset. |
| 100 | +* *fetch* `()` transition call declaration to the fetch stage to declare result consumption multiplicity. |
| 101 | + |
| 102 | +[[r2dbc.datbaseclient.fluent-api.insert]] |
| 103 | +== Inserting Data |
| 104 | + |
| 105 | +Use the `insert()` entry point to insert data. Similar to `select()`, `insert()` allows free-form and mapped object inserts. |
| 106 | + |
| 107 | +Take a look at a simple typed insert operation: |
| 108 | + |
| 109 | +==== |
| 110 | +[source,java] |
| 111 | +---- |
| 112 | +Mono<Void> insert = databaseClient.insert() |
| 113 | + .into(Person.class) <1> |
| 114 | + .using(new Person(…)) <2> |
| 115 | + .then(); <3> |
| 116 | +---- |
| 117 | +<1> Using `Person` with the `into(…)` method sets the `INTO` table based on mapping metadata. It also prepares the insert statement to accept `Person` objects for inserting. |
| 118 | +<2> Provide a scalar `Person` object. Alternatively, you can supply a `Publisher` to execute a stream of `INSERT` statements. This method extracts all non-``null`` values and inserts these. |
| 119 | +<3> Use `then()` to just insert an object without consuming further details. Modifying statements allow consumption of the number of affected rows or tabular results for consuming generated keys. |
| 120 | +==== |
| 121 | + |
| 122 | +Inserts also support untyped operations: |
| 123 | + |
| 124 | +==== |
| 125 | +[source,java] |
| 126 | +---- |
| 127 | +Mono<Void> insert = databaseClient.insert() |
| 128 | + .into("person") <1> |
| 129 | + .value("firstname", "John") <2> |
| 130 | + .nullValue("lastname") <3> |
| 131 | + .then(); <4> |
| 132 | +---- |
| 133 | +<1> Start an insert into the `person` table. |
| 134 | +<2> Provide a non-null value for `firstname`. |
| 135 | +<3> Set `lastname` to `null`. |
| 136 | +<3> Use `then()` to just insert an object without consuming further details. Modifying statements allow consumption of the number of affected rows or tabular results for consuming generated keys. |
| 137 | +==== |
| 138 | + |
| 139 | +[r2dbc.datbaseclient.fluent-api.insert.methods]] |
| 140 | +==== Methods for INSERT operations |
| 141 | + |
| 142 | +The `insert()` entry point exposes some additional methods that provide options for the operation: |
| 143 | + |
| 144 | +* *into* `(Class<T>)` used to specify the target table using a mapped object. Returns results by default as `T`. |
| 145 | +* *into* `(String)` used to specify the target table name. Returns results by default as `Map<String, Object>`. |
| 146 | +* *using* `(T)` used to specify the object to insert. |
| 147 | +* *using* `(Publisher<T>)` used to accept a stream of objects to insert. |
| 148 | +* *table* `(String)` used to override the target table name. |
| 149 | +* *value* `(String, Object)` used to provide a column value to insert. |
| 150 | +* *nullValue* `(String)` used to provide a null value to insert. |
| 151 | +* *map* `(BiFunction<Row, RowMetadata, T>)` used to supply a mapping function to extract results. |
| 152 | +* *then* `()` execute `INSERT` without consuming any results. |
| 153 | +* *fetch* `()` transition call declaration to the fetch stage to declare result consumption multiplicity. |
| 154 | + |
| 155 | +[[r2dbc.datbaseclient.fluent-api.update]] |
| 156 | +== Updating Data |
| 157 | + |
| 158 | +Use the `update()` entry point to update rows. |
| 159 | +Updating data starts with a specification of the table to update accepting `Update` specifying assignments. It also accepts `Criteria` to create a `WHERE` clause. |
| 160 | + |
| 161 | +Take a look at a simple typed update operation: |
| 162 | + |
| 163 | +==== |
| 164 | +[source,java] |
| 165 | +---- |
| 166 | +Person modified = … |
| 167 | +
|
| 168 | +Mono<Void> update = databaseClient.update() |
| 169 | + .table(Person.class) <1> |
| 170 | + .using(modified) <2> |
| 171 | + .then(); <3> |
| 172 | +---- |
| 173 | +<1> Using `Person` with the `table(…)` method sets the table to update based on mapping metadata. |
| 174 | +<2> Provide a scalar `Person` object value. `using(…)` accepts the modified object and derives primary keys and updates all column values. |
| 175 | +<3> Use `then()` to just update rows an object without consuming further details. Modifying statements allow also consumption of the number of affected rows. |
| 176 | +==== |
| 177 | + |
| 178 | +Update also support untyped operations: |
| 179 | + |
| 180 | +==== |
| 181 | +[source,java] |
| 182 | +---- |
| 183 | +Mono<Void> update = databaseClient.update() |
| 184 | + .table("person") <1> |
| 185 | + .using(Update.update("firstname", "Jane")) <2> |
| 186 | + .matching(where("firstname").is("John")) <3> |
| 187 | + .then(); <4> |
| 188 | +---- |
| 189 | +<1> Update table `person`. |
| 190 | +<2> Provide a `Update` definition, which columns to update. |
| 191 | +<3> The issued query declares a `WHERE` condition on `firstname` columns to filter rows to update. |
| 192 | +<4> Use `then()` to just update rows an object without consuming further details. Modifying statements allow also consumption of the number of affected rows. |
| 193 | +==== |
| 194 | + |
| 195 | +[r2dbc.datbaseclient.fluent-api.delete.methods]] |
| 196 | +==== Methods for DELETE operations |
| 197 | + |
| 198 | +The `delete()` entry point exposes some additional methods that provide options for the operation: |
| 199 | + |
| 200 | +* *table* `(Class<T>)` used to specify the target table using a mapped object. Returns results by default as `T`. |
| 201 | +* *table* `(String)` used to specify the target table name. Returns results by default as `Map<String, Object>`. |
| 202 | +* *using* `(T)` used to specify the object to update. Derives criteria itself. |
| 203 | +* *using* `(Update)` used to specify the update definition. |
| 204 | +* *matching* `(Criteria)` used to declare a `WHERE` condition to rows to update. |
| 205 | +* *then* `()` execute `UPDATE` without consuming any results. |
| 206 | +* *fetch* `()` transition call declaration to the fetch stage to fetch the number of updated rows. |
| 207 | + |
| 208 | +[[r2dbc.datbaseclient.fluent-api.delete]] |
| 209 | +== Deleting Data |
| 210 | + |
| 211 | +Use the `delete()` entry point to delete rows. |
| 212 | +Removing data starts with a specification of the table to delete from and optionally accepts a `Criteria` to create a `WHERE` clause. |
| 213 | + |
| 214 | +Take a look at a simple insert operation: |
| 215 | + |
| 216 | +==== |
| 217 | +[source,java] |
| 218 | +---- |
| 219 | +Mono<Void> delete = databaseClient.delete() |
| 220 | + .from(Person.class) <1> |
| 221 | + .matching(where("firstname").is("John") <2> |
| 222 | + .and("lastname").in("Doe", "White")) |
| 223 | + .then(); <3> |
| 224 | +---- |
| 225 | +<1> Using `Person` with the `from(…)` method sets the `FROM` table based on mapping metadata. |
| 226 | +<2> The issued query declares a `WHERE` condition on `firstname` and `lastname` columns to filter rows to delete. |
| 227 | +<3> Use `then()` to just delete rows an object without consuming further details. Modifying statements allow also consumption of the number of affected rows. |
| 228 | +==== |
| 229 | + |
| 230 | +[r2dbc.datbaseclient.fluent-api.delete.methods]] |
| 231 | +==== Methods for DELETE operations |
| 232 | + |
| 233 | +The `delete()` entry point exposes some additional methods that provide options for the operation: |
| 234 | + |
| 235 | +* *from* `(Class<T>)` used to specify the target table using a mapped object. Returns results by default as `T`. |
| 236 | +* *from* `(String)` used to specify the target table name. Returns results by default as `Map<String, Object>`. |
| 237 | +* *matching* `(Criteria)` used to declare a `WHERE` condition to rows to delete. |
| 238 | +* *then* `()` execute `DELETE` without consuming any results. |
| 239 | +* *fetch* `()` transition call declaration to the fetch stage to fetch the number of deleted rows. |
0 commit comments