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.
Consequently, using different connections results in using different transactions.
Spring Data R2DBC includes transaction-awareness in DatabaseClient
that lets you group multiple statements within the same transaction by using {spring-framework-ref}/data-access.html#transaction[Spring’s Transaction Management].
Spring Data R2DBC provides an implementation for ReactiveTransactionManager
with R2dbcTransactionManager
.
The following example shows how to programmatically manage a transaction
ReactiveTransactionManager tm = new R2dbcTransactionManager(connectionFactory);
TransactionalOperator operator = TransactionalOperator.create(tm); (1)
DatabaseClient client = DatabaseClient.create(connectionFactory);
Mono<Void> atomicOperation = client.execute("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)")
.bind("id", "joe")
.bind("name", "Joe")
.bind("age", 34)
.fetch().rowsUpdated()
.then(client.execute("INSERT INTO contacts (id, name) VALUES(:id, :name)")
.bind("id", "joe")
.bind("name", "Joe")
.fetch().rowsUpdated())
.then()
.as(operator::transactional); (2)
});
-
Associate the
TransactionalOperator
with theReactiveTransactionManager
. -
Bind the operation to the
TransactionalOperator
.
{spring-framework-ref}/data-access.html#transaction-declarative[Spring’s declarative Transaction Management] is a less invasive, annotation-based approach to transaction demarcation, as the following example shows:
@Configuration
@EnableTransactionManagement (1)
class Config extends AbstractR2dbcConfiguration {
@Override
public ConnectionFactory connectionFactory() {
return // ...
}
@Bean
ReactiveTransactionManager transactionManager(ConnectionFactory connectionFactory) { (2)
return new R2dbcTransactionManager(connectionFactory);
}
}
@Service
class MyService {
private final DatabaseClient client;
MyService(DatabaseClient client) {
this.client = client;
}
@Transactional
public Mono<Void> insertPerson() {
return client.execute("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)")
.bind("id", "joe")
.bind("name", "Joe")
.bind("age", 34)
.fetch().rowsUpdated()
.then(client.execute("INSERT INTO contacts (id, name) VALUES(:id, :name)")
.bind("id", "joe")
.bind("name", "Joe")
.fetch().rowsUpdated())
.then();
}
}
-
Enable declarative transaction management.
-
Provide a
ReactiveTransactionManager
implementation to back reactive transaction features.