jamesfredley opened a new pull request, #15425:
URL: https://github.com/apache/grails-core/pull/15425

   ## Summary
   
   Fixes #14333 and #11798 - both have the same root cause.
   
   `GrailsOpenSessionInViewInterceptor` only registered a session for the 
**default** datasource's `SessionFactory`. Calling `withSession` on a secondary 
datasource during a web request threw `No Session found for current thread` 
because no session was bound for that `SessionFactory` in 
`TransactionSynchronizationManager`.
   
   ## Root Cause
   
   In `HibernateDatastoreSpringInitializer` (lines 189-194), only one OSIV 
interceptor is created referencing `hibernateDatastore` (the default). The 
interceptor's `setHibernateDatastore()` called 
`setSessionFactory(hibernateDatastore.getSessionFactory())` - binding only the 
default `SessionFactory`.
   
   **Call chain for #14333:**
   1. `GormEntity.withSession` -> `AbstractHibernateGormStaticApi.withSession`
   2. -> `AbstractHibernateDatastore.withSession` -> 
`getHibernateTemplate().execute(callable)`
   3. -> `GrailsHibernateTemplate.doExecute` -> `getSession()` -> 
`sessionFactory.getCurrentSession()`
   4. -> `GrailsSessionContext.currentSession()` checks 
`TransactionSynchronizationManager.getResource(sessionFactory)` for the 
**secondary** `SessionFactory`
   5. -> Nothing bound because OSIV only registered for **default** -> `No 
Session found`
   
   **#11798** has the same root cause: domain class mapped to non-default 
datasource used as command object fails validation because `validate()` needs a 
session but OSIV never opened one.
   
   ## Fix
   
   Modified `GrailsOpenSessionInViewInterceptor` to handle **multiple** 
datasource `SessionFactory` instances in a single interceptor:
   
   - `setHibernateDatastore()` now iterates all connection sources from 
`HibernateDatastore` and stores non-default datasource `SessionFactory` 
references in an `additionalSessionFactories` list
   - `preHandle()` opens sessions and binds `SessionHolder` to 
`TransactionSynchronizationManager` for each additional datasource (skipping 
any that already have a session bound)
   - `postHandle()` flushes additional sessions if their flush mode warrants it
   - `afterCompletion()` unbinds and closes additional sessions in reverse order
   
   This approach keeps a single interceptor bean (backward compatible) and 
avoids the complexity of registering per-datasource OSIV beans.
   
   ## Tests
   
   ### Unit Tests (5 tests)
   - `MultiDataSourceSessionSpec` - Tests OSIV interceptor directly:
     - Default datasource session binding
     - Secondary datasource session binding
     - Cleanup of all datasource sessions
     - Skip-if-already-bound behavior
     - CRUD operations on secondary datasource
   
   ### Functional/Integration Tests (4 tests via HTTP)
   - `MultiDataSourceWithSessionSpec` - Tests through actual HTTP requests to a 
controller:
     - `withSession` on secondary datasource does not throw (covers #14333)
     - CRUD via `withSession` on secondary datasource
     - Domain validation via `withSession` on secondary datasource (covers 
#11798)
     - `withSession` works after `executeUpdate` on secondary datasource
   
   ### Existing Tests
   - All 10 `grails-data-hibernate5` tests pass
   - All 42 `grails-data-hibernate5-core` connection tests pass
   - All 6 existing `grails-multiple-datasources` integration tests pass
   
   ## Changed Files
   
   | File | Change |
   |------|--------|
   | `GrailsOpenSessionInViewInterceptor.java` | Multi-datasource OSIV support |
   | `grails-data-hibernate5/grails-plugin/build.gradle` | Added servlet-api 
test dependency |
   | `MultiDataSourceSessionSpec.groovy` | New unit tests |
   | `SecondaryBookController.groovy` | New test controller |
   | `UrlMappings.groovy` | URL mappings for test controller |
   | `MultiDataSourceWithSessionSpec.groovy` | New functional tests via HTTP |
   | `grails-multiple-datasources/build.gradle` | Added HTTP client + URL 
mappings dependencies |


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to