SessionImpl.initializeCollection() does not release JDBC connection (if outside of a transaction)
With ConnectionReleaseMode.AFTER_TRANSACTION (the default), Hibernate does not release the JDBC connection when resolving a lazily loaded collection, if this happens outside of an active transaction.
First, I will describe, what happens if Hibernate executes a query outside of a transaction. Then I will describe how lazy collection loading behaves differently.
When the method list of a query (QueryImpl.java) is getting called, Hibernate delegates to SessionImpl.list(). After having loaded the list, SessionImpl.list() calls SessionImpl.afterOperation(), which calls jdbcContext.afterNontransactionalQuery() (as there is no active transaction). This leads to ConnectionManager.afterTransaction() which releases the JDBC connection. This is exactly what I expect to happen.
Now to the lazily loaded collection:
Hibernate.initialize(collection) and person.getPets().size() both end up in SessionImpl.initializeCollection(). This call gets dispatched down to Loader.doQueryAndInitializeNonLazyCollections() and Loader.doQuery(). The important fact is, that ConnectionManager.afterTransaction() never gets called (like in the query-example above).
The result is, that the JDBC connection is not released.
IMHO resolving of a lazily loaded collection should behave like executing a query. SessionImpl.initializeCollection() should call SessionImpl.afterOperation() in the same way as SessionImpl.list() is doing this.
I will attach a small demo application which shows the difference (concerning ConnectionManager) between queries and collections.
started to work on a PR but we decided it's too risky to get included into 5.3 at this point. There are quite a lot of open questions about this issue so let's target 5.4 and revisit it later.
It seems to me like closing the Hibernate Session also fixes the issue. You don't get an open stale connection for each and every of multiple lazy initializations within one single Session do you?
Added a test case for Hibernate 5.3.1.
Where did you find 5.4.1?
This bug is still present in 5.4.1.Final and is easily reproducible as the original post describes: just call a lazy load getter outside of an active transaction and you get a connection leak, causing the connection pool to get filled very qickly (using hikari in our application). I believe this goes hand in hand with the fact that org.hibernate.ConnectionReleaseMode by default only releases the connection after a transaction. Since there is no transaction, connection leaks.
Putting the lazy load getter calls inside a transaction solves this problem, however, one should not really need to start transactions just to call some getters on an entity (I think?).
Eclipselink in comparision releases the connection properly as one would expect. Hopefully hibernate would be willing to change this behaviour or at least add a release mode option that would mimic the desired effect specified in the original post.
I can also provide a test case for hibernate 5, although I don't think anything changed since the original report.
As part of verifying that this issue affects 5.0, please just set the "Affects version". Leave the "verify-affects-5.0" label and leave the issue in "Awaiting Response" status; these are critical for us to be able to track these verifications and triage them. Thanks.
This bug report does not indicate that the reported issue affects version 5.x. Versions prior to 5.x are no longer maintained. It would be a great help to the Hibernate team and community for someone to verify that the reported issue still affects version 5.x. If so, please add the 5.x version that you verified with to the list of affected-versions and attach the (preferably SSCCE) test case you used to do the verification to the report; from there the issues will be looked at during our triage meetings.
For details, see http://in.relation.to/2015/10/27/great-jira-cleanup-2015/