fix: resolve JPA transaction managers for Storm-initiated transactions - #442
Merged
Merged
Conversation
The transaction bridge resolved the manager for a DataSource by filtering on DataSourceTransactionManager. An application with spring-boot-starter-data-jpa gets a JpaTransactionManager instead, so Storm-initiated transaction blocks failed with "No TransactionManager found for DataSource" while @transactional kept working through DataSourceUtils. Resolution now matches any ResourceTransactionManager working directly on the DataSource, plus a JpaTransactionManager whose entity manager factory is backed by it. The JPA branch sits behind a class-presence check with spring-orm as an optional dependency, so JDBC-only applications are unaffected. When several managers own the same DataSource, resolution fails fast naming the candidates: the choice decides which manager completes the transaction, so it must be made by configuration rather than list order. The transaction auto-configuration also gains ordering hints for the Hibernate JPA auto-configuration (Spring Boot 3 and 4 locations), so @ConditionalOnBean(PlatformTransactionManager) sees the JPA-registered manager. Fixes #383
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
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.
Problem
SpringTransactionContext.resolveTransactionManagerfiltered the application's transaction managers onDataSourceTransactionManager. An application withspring-boot-starter-data-jpagets aJpaTransactionManager(Boot'sDataSourceTransactionManagerAutoConfigurationbacks off), so Storm-initiatedtransaction { }blocks failed with "No TransactionManager found for DataSource". The asymmetry made it hard to diagnose:@Transactionalplus Storm kept working throughDataSourceUtils, so only Storm-initiated transactions failed, and CI never saw it because every test config registered aDataSourceTransactionManagerexplicitly.Fix
Resolution now matches a manager that owns the touched
DataSource:ResourceTransactionManagerworking directly on theDataSource, which coversDataSourceTransactionManagerandJdbcTransactionManager;JpaTransactionManagerwhose entity manager factory is backed by theDataSource, which is what Spring Boot registers when JPA is on the class path.The JPA branch is checked first (
JpaTransactionManageris itself aResourceTransactionManager, andgetResourceFactory()throws when no entity manager factory is set) and sits behind a class-presence check, with spring-orm as an optional (provided,requires static) dependency, so JDBC-only applications are unaffected. A manager that cannot report its resource factory counts as owning nothing rather than failing resolution.When several managers own the same
DataSource, resolution fails fast naming the candidates instead of picking by list order: the choice decides which manager completes the transaction (a JPA manager flushes the persistence context on commit), so it must be made by configuration. The error points at the remedy: keep one manager perDataSource, or define aTransactionTemplateProviderbean constructed with the manager that must own Storm-initiated transactions.StormTransactionAutoConfigurationadditionally gains name-based ordering hints forHibernateJpaAutoConfiguration(Spring Boot 3 and 4 locations), so@ConditionalOnBean(PlatformTransactionManager)sees the JPA-registered manager instead of silently backing off.Behavior change
An application that registers both a
DataSourceTransactionManagerand aJpaTransactionManagerover the sameDataSource— the workaround for this very issue — previously had Storm silently use the JDBC one; it now gets the descriptive ambiguity error. Removing the redundant JDBC manager restores a single, unambiguous owner.Tests
SpringJpaTransactionBridgeTest:transaction { }under a realJpaTransactionManager(Hibernate entity manager factory, no JPA entities) — commit, rollback-only, REQUIRES_NEW, joining a JPA-managed Spring transaction, a JPA manager for a differentDataSourcebeing skipped, and the ambiguity fail-fast.StormTransactionAutoConfigurationTest:ApplicationContextRunneractivation with a JDBC manager, with a JPA manager, and back-off without any; the Storm auto-configuration is listed first so the declared ordering hints are what the test proves.Fixes #383