homicki opened a new issue, #16571:
URL: https://github.com/apache/grails-core/issues/16571
### Expected Behavior
In this report, the request session is the Hibernate session that open
session in view (OSIV) binds. The new session is the session that
`withNewSession` opens. The inner transaction is the transaction that
`withTransaction` starts in the new session.
After `withNewSession { withTransaction { … } }` completes, the OSIV-bound
request session remains usable. Subsequent queries and `refresh()` succeed.
### Actual Behaviour
Code runs in the request session without a transaction. It runs and ends a
transaction on the request session. It then calls `withNewSession {
withTransaction { } }`. Inside that block, it reads a lazy association of an
entity that the request session loaded.
After the block, the next statement of the request session fails because its
connection is closed. `GrailsHibernateTemplate` then throws a
`NullPointerException` ("Cannot throw exception because the return value of ...
convertHibernateAccessException(...) is null"). Before the
`NullPointerException`, the log shows the JDBC error. On H2, the error is `The
object is already closed [90007-240]`.
### Steps To Reproduce
1. Clone
https://github.com/homicki/grails8-withnewsession-closed-connection-repro and
run `./gradlew bootRun`.
2. `curl -i http://localhost:8080/repro` returns `500` with the
`NullPointerException` above.
3. `curl -i "http://localhost:8080/repro?lazyLoad=before"` returns `200`.
This control reads the association before the block.
The action runs in the OSIV request session without a transaction.
`BootStrap` seeds one book and its author, and `Book.author` is lazy:
```groovy
Book book = Book.first()
Author.withTransaction { Author.count() } // ends a transaction on the
request session
Book.withNewSession {
Book.withTransaction {
book.author.name // lazy load through the
request session
new Author(name: 'Tenar').save()
}
}
Book.count() // fails: the connection is
closed
```
The reproduction fails when all three of these conditions are true: a
transaction ended on the request session earlier, the first use of the request
session after that transaction is inside the inner transaction, and the inner
block uses `withNewSession`. In the tested variants, `withNewTransaction` alone
succeeds, and loading the association before or after the block also succeeds.
Proposed fix: before executeWithNewSession unbinds the request session, make
an open, disconnected request session get its own connection. I have this fix
with a spec that fails without it and passes with it, written with AI
assistance. If the team agrees with this approach, I can submit it as a pull
request.
### Environment Information
- Grails 8.0.x
- Hibernate ORM 7.4
- Spring Framework 7.0
- MariaDB 10.11 and MySQL 8.4
- Groovy 5.1
- JDK 25
### Example Application
https://github.com/homicki/grails8-withnewsession-closed-connection-repro
### Version
8.0.0
--
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]