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

   Fixes #16571.
   
   ## Problem
   
   When a transaction on a request-bound Hibernate session completes, Spring's 
`disconnectOnCompletion(session)` disconnects the session's physical JDBC 
connection, allowing it to be returned to the connection pool until needed.
   
   If the application subsequently opens a nested `withNewSession` and inside 
it starts a transaction (`withTransaction`), Spring's 
`TransactionSynchronizationManager` binds the inner transaction's physical 
connection to the thread under the `dataSource` resource key.
   
   If an entity loaded earlier in the outer request session is touched inside 
that nested scope (for example, accessing a lazy association like 
`book.author.name`), the outer session detects that it is physically 
disconnected and requests a connection from the `dataSource`. Because the 
`dataSource` is wrapped in Spring's `TransactionAwareDataSourceProxy`, it 
returns the thread-bound connection of the inner transaction.
   
   When the inner transaction completes, it closes its connection. The outer 
session is left holding that closed connection, and subsequent operations fail 
with:
   ```
   java.sql.SQLException: PooledConnection has already been closed.
   ```
   Additionally, if `convertJdbcAccessException` returned `null` because the 
database vendor error code could not be mapped, 
`GrailsHibernateTemplate.doExecute` would throw `NullPointerException` when 
trying to throw the result of `convertHibernateAccessException`.
   
   ## Change
   
   1. In `GrailsHibernateTemplate.executeWithNewSession`, before unbinding the 
existing session holder and entering the new session scope, check whether the 
outer session is open but physically disconnected 
(`!logicalConnection.isPhysicallyConnected()`). If so, acquire and initialize 
its physical connection (`con.getMetaData()`) from the data source pool 
beforehand. This ensures the outer session holds its own independent connection 
and does not borrow the inner transaction's thread-bound connection during lazy 
loading.
   2. In `GrailsHibernateTemplate.convertHibernateAccessException`, fall back 
to `SessionFactoryUtils.convertHibernateAccessException(ex)` if 
`convertJdbcAccessException` returns `null`.
   3. In `GrailsHibernateTemplate.doExecute`, fall back to `new 
HibernateSystemException(ex)` if `convertHibernateAccessException` returns 
`null`.
   
   ## Tests
   
   - `WithNewSessionLazyLoadSpec`: reproduces the scenario where a lazy 
association on an outer request-bound session entity is loaded inside a nested 
`withNewSession { withTransaction { ... } }`, and verifies that the outer 
session remains open and usable for subsequent queries and updates.
   - `GrailsHibernateTemplateSpec`: added test verifying fallback to 
`SessionFactoryUtils` when JDBC exception translation returns null.
   


-- 
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