Named parameters can be provided by name and by index. Repository query methods bind parameters by name if a named parameter can be found.
If parameters are bound by index, then the parameter name is looked up by index (index corresponds with the order of parameter name discovery when parsing the query). and bound to the parameter.
We now exclude byte[] properties from being mapped to array types. To map data to a 1-dimensional BYTE[] Postgres type, properties can be declared as Collection<Byte> or Byte[].
DatabaseClient.BindSpec.bind(…) (execute) and DatabaseClient.GenericInsertSpec.value(…) (insert) now consistently accept SettableValue for scalar and absent values.
This change allows us to provide a Kotlin extension leveraging reified generics to provide the type of a value even if it is null to construct an appropriate SettableValue for fluent API usage.
We now support Converters on Entity-level if object materialization/dematerialization is handled by application code. We're using a custom R2DBC MappingContext to create mapping metadata for types that have custom converters registered.
We now allow multiple usages of the same named parameter if the underlying database supports identifiable placeholders.
SELECT * FROM person where name = :id or lastname = :id
gets translated to
SELECT * FROM person where name = $1 or lastname = $1
RowsFetchSpec.awaitOne() and RowsFetchSpec.awaitFirst() now throw EmptyResultDataAccessException instead of NoSuchElementException to consistently use Spring DAO exceptions.
Named parameter resolution is now provided as part of ReactiveDataAccessStrategy. This allows us to hide implementation internals (bind markers). DatabaseClient allows configuration whether to use named parameter expansion.
Detailed configuration of named parameter support is now moved to DefaultReactiveDataAccessStrategy.
Original pull request: #105.
We now provide an abstract base class for ConnectionFactory routing. Routing keys are typically obtained from a subscriber context. AbstractRoutingConnectionFactory is backed by either a map of string identifiers or connection factories. When using string identifiers, these can map agains e.g. Spring bean names that can be resolved using BeanFactoryConnectionFactoryLookup.
class MyRoutingConnectionFactory extends AbstractRoutingConnectionFactory {
@Override
protected Mono<Object> determineCurrentLookupKey() {
return Mono.subscriberContext().filter(it -> it.hasKey(ROUTING_KEY)).map(it -> it.get(ROUTING_KEY));
}
}
@Bean
public void routingConnectionFactory() {
MyRoutingConnectionFactory router = new MyRoutingConnectionFactory();
Map<String, ConnectionFactory> factories = new HashMap<>();
ConnectionFactory myDefault = …;
ConnectionFactory primary = …;
ConnectionFactory secondary = …;
factories.put("primary", primary);
factories.put("secondary", secondary);
router.setTargetConnectionFactories(factories);
router.setDefaultTargetConnectionFactory(myDefault);
return router;
}
Original pull request: #132.
We now use Spring's spring.factories mechanism to register and lookup R2dbcDialectProvider implementations.
This allows a pluggable model for third party implementations to ship R2dbcDialect support that can be auto-discovered.
Original pull request: #127.
We now reuse the existing Dialect infrastructure provided by Spring Data Relational to enhance it for R2DBC specifics such as bind markers.
Original pull request: #125.
We compressed client.execute().sql(…) to client.execute(…) to not require the intermediate execute() step but rather accept the SQL to execute directly.
Original pull request: #112.
We now correctly consider built-in converters for simple types that are read through DatabaseClient. Simple type projections typically select a single column and expect a result stream of simple values such as selecting a count and retrieving a Mono<Long>.
Extract method references for better readability. Add missing tests and update documentation.
Add delay for transactional MySql tests to avoid failures due to potentially delayed transaction id storage within the database.
Original Pull Request: #107
We now support R2DBC transaction management through ConnectionFactoryTransactionManager which is a ReactiveTransactionManager implementation to be used with TransactionalOperator and Spring's declarative transaction management.
ConnectionFactoryTransactionManager tm = new ConnectionFactoryTransactionManager(connectionFactory);
TransactionalOperator operator = TransactionalOperator.create(tm);
DatabaseClient db = DatabaseClient.create(connectionFactory);
Mono<Void> atomicOperation = db.execute().sql("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)")
.bind("id", "joe")
.bind("name", "Joe")
.bind("age", 34)
.fetch().rowsUpdated()
.then(db.execute().sql("INSERT INTO contacts (id, name) VALUES(:id, :name)")
.bind("id", "joe")
.bind("name", "Joe")
.fetch().rowsUpdated())
.then()
.as(operator::transactional);
Original Pull Request: #107
Incorporated feedback from review. Polished documentation and Javadoc. Minor code improvements restructuring for better readability. Removed unused methods types. Some polishing for compiler warnings.
Original pull request: #106.
Document fluent API. Add fluent API for update. Introduce StatementMapper. Migrate Insert to StatementMapper. Refactoring and cleanup. Migrate Select to StatementMapper.
Original pull request: #106.
We now support Criteria creation and mapping to express where conditions with a fluent API.
databaseClient.select().from("legoset")
.where(Criteria.of("name").like("John%").and("id").lessThanOrEquals(42055));
databaseClient.delete()
.from(LegoSet.class)
.where(Criteria.of("id").is(42055))
.then()
databaseClient.delete()
.from(LegoSet.class)
.where(Criteria.of("id").is(42055))
.fetch()
.rowsUpdated()
Original pull request: #106.
We now apply bindings to a BindTarget that can be overridden without the need to implement all Statement methods.
PreparedOperation no longer has a direct dependency on R2DBC Statement.
Original pull request: #82.