Skip to content

Commit 366c10b

Browse files
mp911deschauder
authored andcommitted
#189 - Accept StatementFilterFunction in DatabaseClient.
We now accept StatementFilterFunction and ExecuteFunction via DatabaseClient to filter Statement execution. StatementFilterFunctions can be used to pre-process the statement or post-process Result objects. databaseClient.execute(…) .filter((s, next) -> next.execute(s.returnGeneratedValues("my_id"))) .filter((s, next) -> next.execute(s.fetchSize(25))) databaseClient.execute(…) .filter(s -> s.returnGeneratedValues("my_id")) .filter(s -> s.fetchSize(25)) Original pull request: #308.
1 parent e56f126 commit 366c10b

File tree

9 files changed

+434
-42
lines changed

9 files changed

+434
-42
lines changed

Diff for: src/main/asciidoc/new-features.adoc

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
== What's New in Spring Data R2DBC 1.1.0 RELEASE
66

77
* Introduction of `R2dbcEntityTemplate` for entity-oriented operations.
8-
* Support interface projections with `DatabaseClient.as(…)`
8+
* Support interface projections with `DatabaseClient.as(…)`.
9+
* <<r2dbc.datbaseclient.filter,Support for `ExecuteFunction` and `StatementFilterFunction` via `DatabaseClient.filter(…)`>>.
910

1011
[[new-features.1-0-0-RELEASE]]
1112
== What's New in Spring Data R2DBC 1.0.0 RELEASE

Diff for: src/main/asciidoc/reference/r2dbc-sql.adoc

+35-3
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ In JDBC, the actual drivers translate `?` bind markers to database-native marker
134134
135135
Spring Data R2DBC lets you use native bind markers or named bind markers with the `:name` syntax.
136136
137-
Named parameter support leverages a `R2dbcDialect` instance to expand named parameters to native bind markers at the time of query execution, which gives you a certain degree of query portability across various database vendors.
137+
Named parameter support leverages a `R2dbcDialect` instance to expand named parameters to native bind markers at the time of query execution, which gives you a certain degree of query portability across various database vendors.
138138
****
139139

140140
The query-preprocessor unrolls named `Collection` parameters into a series of bind markers to remove the need of dynamic query creation based on the number of arguments.
@@ -159,7 +159,7 @@ tuples.add(new Object[] {"John", 35});
159159
tuples.add(new Object[] {"Ann", 50});
160160
161161
db.execute("SELECT id, name, state FROM table WHERE (name, age) IN (:tuples)")
162-
.bind("tuples", tuples);
162+
.bind("tuples", tuples)
163163
----
164164
====
165165

@@ -171,6 +171,38 @@ The following example shows a simpler variant using `IN` predicates:
171171
[source,java]
172172
----
173173
db.execute("SELECT id, name, state FROM table WHERE age IN (:ages)")
174-
.bind("ages", Arrays.asList(35, 50));
174+
.bind("ages", Arrays.asList(35, 50))
175175
----
176176
====
177+
178+
[[r2dbc.datbaseclient.filter]]
179+
== Statement Filters
180+
181+
You can register a `Statement` filter (`StatementFilterFunction`) through `DatabaseClient` to intercept and modify statements in their execution, as the following example shows:
182+
183+
====
184+
[source,java]
185+
----
186+
db.execute("INSERT INTO table (name, state) VALUES(:name, :state)")
187+
.filter((s, next) -> next.execute(s.returnGeneratedValues("id")))
188+
.bind("name", …)
189+
.bind("state", …)
190+
----
191+
====
192+
193+
`DatabaseClient` exposes also simplified `filter(…)` overload accepting `UnaryOperator<Statement>`:
194+
195+
====
196+
[source,java]
197+
----
198+
db.execute("INSERT INTO table (name, state) VALUES(:name, :state)")
199+
.filter(s -> s.returnGeneratedValues("id"))
200+
.bind("name", …)
201+
.bind("state", …)
202+
203+
db.execute("SELECT id, name, state FROM table")
204+
.filter(s -> s.fetchSize(25))
205+
----
206+
====
207+
208+
`StatementFilterFunction` allow filtering of the executed `Statement` and filtering of `Result` objects.

Diff for: src/main/java/org/springframework/data/r2dbc/core/DatabaseClient.java

+42-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import io.r2dbc.spi.ConnectionFactory;
1919
import io.r2dbc.spi.Row;
2020
import io.r2dbc.spi.RowMetadata;
21+
import io.r2dbc.spi.Statement;
2122
import reactor.core.publisher.Mono;
2223

2324
import java.util.Arrays;
@@ -26,6 +27,7 @@
2627
import java.util.function.Consumer;
2728
import java.util.function.Function;
2829
import java.util.function.Supplier;
30+
import java.util.function.UnaryOperator;
2931

3032
import org.reactivestreams.Publisher;
3133

@@ -37,6 +39,7 @@
3739
import org.springframework.data.r2dbc.query.Update;
3840
import org.springframework.data.r2dbc.support.R2dbcExceptionTranslator;
3941
import org.springframework.data.relational.core.sql.SqlIdentifier;
42+
import org.springframework.util.Assert;
4043

4144
/**
4245
* A non-blocking, reactive client for performing database calls requests with Reactive Streams back pressure. Provides
@@ -142,6 +145,16 @@ interface Builder {
142145
*/
143146
Builder exceptionTranslator(R2dbcExceptionTranslator exceptionTranslator);
144147

148+
/**
149+
* Configures a {@link ExecuteFunction} to execute {@link Statement} objects.
150+
*
151+
* @param executeFunction must not be {@literal null}.
152+
* @return {@code this} {@link Builder}.
153+
* @since 1.1
154+
* @see Statement#execute()
155+
*/
156+
Builder executeFunction(ExecuteFunction executeFunction);
157+
145158
/**
146159
* Configures a {@link ReactiveDataAccessStrategy}.
147160
*
@@ -186,7 +199,7 @@ interface Builder {
186199
/**
187200
* Contract for specifying a SQL call along with options leading to the exchange.
188201
*/
189-
interface GenericExecuteSpec extends BindSpec<GenericExecuteSpec> {
202+
interface GenericExecuteSpec extends BindSpec<GenericExecuteSpec>, StatementFilterSpec<GenericExecuteSpec> {
190203

191204
/**
192205
* Define the target type the result should be mapped to. <br />
@@ -231,7 +244,7 @@ interface GenericExecuteSpec extends BindSpec<GenericExecuteSpec> {
231244
/**
232245
* Contract for specifying a SQL call along with options leading to the exchange.
233246
*/
234-
interface TypedExecuteSpec<T> extends BindSpec<TypedExecuteSpec<T>> {
247+
interface TypedExecuteSpec<T> extends BindSpec<TypedExecuteSpec<T>>, StatementFilterSpec<TypedExecuteSpec<T>> {
235248

236249
/**
237250
* Define the target type the result should be mapped to. <br />
@@ -866,4 +879,31 @@ interface BindSpec<S extends BindSpec<S>> {
866879
*/
867880
S bindNull(String name, Class<?> type);
868881
}
882+
883+
/**
884+
* Contract for applying a {@link StatementFilterFunction}.
885+
*
886+
* @since 1.1
887+
*/
888+
interface StatementFilterSpec<S extends StatementFilterSpec<S>> {
889+
890+
/**
891+
* Add the given filter to the end of the filter chain.
892+
*
893+
* @param filter the filter to be added to the chain.
894+
*/
895+
default S filter(UnaryOperator<Statement> filter) {
896+
897+
Assert.notNull(filter, "Statement FilterFunction must not be null!");
898+
899+
return filter((statement, next) -> next.execute(filter.apply(statement)));
900+
}
901+
902+
/**
903+
* Add the given filter to the end of the filter chain.
904+
*
905+
* @param filter the filter to be added to the chain.
906+
*/
907+
S filter(StatementFilterFunction filter);
908+
}
869909
}

0 commit comments

Comments
 (0)