Transactions and lifecycle
Latest stable Based on Bluetape4k release 1.11.0
The smallest atomic boundary
Section titled “The smallest atomic boundary”withTransactionSuspend creates a TransactionalOperator for the DatabaseClient.connectionFactory. Successful completion commits; an exception rolls back.
val result = client.databaseClient.withTransactionSuspend { client.databaseClient .sql("INSERT INTO accounts (owner, balance) VALUES (:owner, :balance)") .bind("owner", "kim") .bind("balance", 1000) .fetch() .awaitRowsUpdated()
client.databaseClient .sql("INSERT INTO audit_log (message) VALUES (:message)") .bind("message", "account created") .fetch() .awaitRowsUpdated()
"created"}Place the boundary around the smallest service operation whose writes must succeed together. The block’s ReactiveTransaction can mark rollback-only state when required.
Transaction manager cache and direct connections
Section titled “Transaction manager cache and direct connections”Version 1.11.0 caches one manager per ConnectionFactory in a locked WeakHashMap. This avoids repeated manager construction but does not own the pool lifecycle. withTransactionSuspending is deprecated; use withTransactionSuspend.
getConnectionAndAwait and releaseConnectionAndAwait recognize Spring transaction-bound connections. fetchConnectionAndAwait calls ConnectionFactory.create() directly; the caller must close the result and must not assume it is the current transaction connection. Prefer the same DatabaseClient inside a transaction unless low-level access is necessary.
Schema and data initialization
Section titled “Schema and data initialization”resourceDatabasePopulatorOf wraps SQL resources, compositeDatabasePopulatorOf orders multiple populators, and connectionFactoryInitializer attaches them to a factory.
val initializer = connectionFactoryInitializer(connectionFactory) { setDatabasePopulator( compositeDatabasePopulatorOf( resourceDatabasePopulatorOf(ClassPathResource("schema.sql")), resourceDatabasePopulatorOf(ClassPathResource("data.sql")), ) )}Do not let this initializer and a migration tool both own production schema state. Select one source of truth and test execution timing and repeat safety.
1.11.0 auto-configuration
Section titled “1.11.0 auto-configuration”R2dbcClientAutoConfiguration registers R2dbcClient from DatabaseClient, R2dbcEntityTemplate, and MappingR2dbcConverter when the DatabaseClient class is present. Version 1.11.0 has no @ConditionalOnMissingBean(R2dbcClient::class). A user bean does not automatically cause back-off; explicitly exclude or reconcile the auto-configuration.
Preserve transaction exceptions and cancellation. Retry an entire transaction only when the operation is idempotent and the failure is known to be transient.
Sources and tests
Section titled “Sources and tests”TransactionSupport.ktConnectionFactoryUtils.ktR2dbcClientAutoConfiguration.ktConnectionFactoryInitializer.ktTransactionSupportTest.ktConnectionInitTest.kt
Next chapter
Section titled “Next chapter”Continue to the R2DBC ecosystem path.