feat!: instance-scoped integration points with lazily bound transaction scopes#218
Open
zantvoort wants to merge 4 commits into
Open
feat!: instance-scoped integration points with lazily bound transaction scopes#218zantvoort wants to merge 4 commits into
zantvoort wants to merge 4 commits into
Conversation
…nsaction scopes
Replace JVM-global ServiceLoader winner-takes-all resolution of ConnectionProvider
and TransactionTemplateProvider with instance-scoped strategies on a new
ORMTemplate.builder(), mirrored in the java21 and kotlin facades. ServiceLoader
remains the fallback for templates built without explicit strategies; provider
enablement is re-evaluated per resolution and ambiguous resolution fails fast
naming the candidates.
Add two new template-scoped SPIs: ExceptionMapper (maps execution failures to the
thrown exception, enabling platform hierarchies such as Spring's
DataAccessException) and QueryObserver (observes query executions for metrics and
tracing bindings).
Introduce TransactionScope: transaction { } blocks now open a provider-agnostic
pending scope that is materialized by the first template that executes inside it,
through that template's transaction provider. Scopes nest, join by provider
instance identity, fail fast on cross-provider mixing, enforce timeouts from
block entry, and propagate rollback-only marks including the joined-scope
distinction that surfaces UnexpectedRollbackException. The TransactionTemplate
SPI is reshaped from callback-wrapping execute() to open()/complete() handles to
support the lazy binding.
Remove all reflective Spring probes from storm-core and storm-kotlin: templates
never silently enlist in Spring transactions based on classpath presence. The
storm-core test suite now runs on the classpath with test-scoped Spring-aware
providers registered via META-INF/services, preserving transaction-per-test
isolation under @DataJpaTest.
…rategies storm-spring: rename TransactionAwareConnectionProviderImpl to the public st.orm.spring.SpringConnectionProvider and add SpringTransactionTemplateProvider, a non-reflective port of the TransactionSynchronizationManager-linked context that previously lived in storm-core, preserving transaction-scoped entity caching for Spring-managed transactions. The ServiceLoader registration is removed. storm-kotlin-spring: delete SpringTransactionConfiguration's static transaction manager list and @EnableTransactionIntegration. The providers become public, constructor-injected classes (SpringConnectionProvider, SpringTransactionTemplateProvider) that carry their application context's transaction managers; SpringTransactionContext is restructured to the open/complete SPI. springOrmTemplate() is the canonical plain-Spring composition. ServiceLoader registrations are removed. Starters: StormTransactionAutoConfiguration now contributes Spring-aware ConnectionProvider and TransactionTemplateProvider beans (backed off via @ConditionalOnMissingBean), and both StormAutoConfigurations consume the strategy beans, plus optional ExceptionMapper and QueryObserver beans, through the template builder. The Java starter gains transaction-scoped entity caching under @transactional via the ported provider. storm-ktor: install(Storm) gains connectionProvider, transactionTemplateProvider, exceptionMapper and queryObserver slots and composes the template with explicit plugin-scoped provider instances.
…ration Add DualContextIsolationTest: two application contexts in one JVM bind transactions to their own managers, survive each other's shutdown, fail fast when templates with different transaction providers share one block, and standalone templates no longer silently enlist in Spring-managed transactions. Add IntegrationStrategiesTest covering builder precedence over ServiceLoader discovery, ordered fallback resolution, the ExceptionMapper contract (context propagation, mapper failures never mask the original error), and the QueryObserver lifecycle (stream-close, error path, observer containment). The test suites of storm-core, the JSON modules and the dialect modules relied on the removed reflective Spring probe for their transaction-per-test isolation under @DataJpaTest. They now register an explicit test-scoped Spring-aware ConnectionProvider via META-INF/services and run surefire on the classpath so ServiceLoader picks it up.
…n semantics Replace the @EnableTransactionIntegration guidance with springOrmTemplate for plain Spring and the starter's provider beans for Spring Boot, document that transaction blocks bind to the first template that executes inside them, and add the changelog entries for the integration-point rework.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Makes the framework integration points instance-scoped configuration on the template, with
ServiceLoaderdiscovery demoted to fallback-only. Fixes #198.Core
ORMTemplate.builder(dataSource | connection)(core, mirrored in the Java 21 and Kotlin APIs) with four instance-scoped strategy slots:connectionProvider,transactionTemplateProvider,exceptionMapper,queryObserver. Allof(...)overloads stay and keep their behavior; templates built without explicit strategies fall back toServiceLoaderdiscovery.ExceptionMapper— maps execution failures to the exception thrown to the caller (SQL diagnostics are attached by the framework before mapping; mapper failures never mask the original error).QueryObserver— observes query executions (operation, data type, execution kind, timing, outcome) with strict containment: observer failures never affect query execution.Providersrework — the JVM-wideAtomicReferencepin for the winningConnectionProvideris gone,isEnabled()is re-evaluated per resolution instead of being frozen at firstServiceLoaderload, and ambiguous winner-takes-all resolution now fails fast naming the candidates.QueryImplno longer resolves a transaction template in static init.DataSourceUtils/TransactionSynchronizationManagerbridges are deleted from storm-core and storm-kotlin. Templates never silently enlist in Spring transactions based on classpath presence.Transactions: lazily bound scopes
transaction { }/transactionBlocking { }keep their exact signatures and remain the only transaction API. Opening a block now records a pendingTransactionScope(options only); the first template that executes inside the block materializes the scope through its own transaction provider. Scopes nest with full propagation semantics (enclosing scopes materialize outermost-down, preserving eager-frame behavior for REQUIRED/MANDATORY/NEVER/REQUIRES_NEW), join by provider instance identity, fail fast on cross-provider mixing, enforce timeouts from block entry, and propagate rollback-only marks including the joined-scope distinction behindUnexpectedRollbackException. TheTransactionTemplateSPI is reshaped from callback-wrappingexecute()toopen()/complete()handles to make the lazy binding possible.Integration modules as composition roots
SpringTransactionConfiguration(the static, JVM-global transaction manager list) and@EnableTransactionIntegrationare deleted. The providers become public, constructor-injectedst.orm.spring.SpringConnectionProvider/SpringTransactionTemplateProvidercarrying their own context's managers;springOrmTemplate(dataSource) { managers }is the canonical plain-Spring composition.TransactionAwareConnectionProviderImplbecomes the publicSpringConnectionProvider, and the reflective Spring-linked context from core is ported as a realSpringTransactionTemplateProvider, so Java applications keep transaction-scoped entity caching under@Transactional.StormTransactionAutoConfigurationcontributes the Spring-aware provider beans when aPlatformTransactionManageris present (user-defined beans win via@ConditionalOnMissingBean), and bothStormAutoConfigurations consume the strategy beans plus optionalExceptionMapper/QueryObserverbeans through the builder. Nothing is registered globally: each application context gets its own, correctly matched transaction integration.install(Storm)gainsconnectionProvider,transactionTemplateProvider,exceptionMapperandqueryObserverslots and composes the template with explicit plugin-scoped provider instances.META-INF/servicesregistrations are removed; storm-kotlin's platform-neutral registrations remain as the sanctioned fallback for standalone use.Tests
DualContextIsolationTestproves the motivating scenario: two application contexts in one JVM with correctly matched transaction managers, isolation across context close, fail-fast on cross-provider mixing, and no silent enlistment of standalone templates.IntegrationStrategiesTestcovers builder precedence overServiceLoader, theExceptionMappercontract, and theQueryObserverlifecycle (stream-close, error path, containment).@DataJpaTest; they now register an explicit test-scoped Spring-awareConnectionProviderviaMETA-INF/servicesand run surefire on the classpath soServiceLoaderpicks it up.Behavior changes (release notes in CHANGELOG.md)
ORMTemplate.of(dataSource)) inside a Spring application no longer join Spring transactions; compose withspringOrmTemplateor the builder to integrate. The old "programmatic and Spring managed transactions cannot be mixed" guard is superseded by the per-block provider check.transaction { }variant remains unsupported with Spring-managed transactions and now fails at materialization with a clear message.docs/only; visible at/docs/nextuntil the 1.13 release): Spring integration recipes, transaction binding semantics, module descriptions.Full reactor build passes (
mvn clean install), including the dialect suites against live Oracle/PostgreSQL/MySQL/MariaDB/MSSQL databases: ~10,500 tests, 0 failures.