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]