grails-data-mapping
grails-data-mapping copied to clipboard
withNewSession behavior is inconsistent with the documentation and seems contrary to it's purpose
The docs for withNewSession say (emphasis added):
Provides a way to execute code within the context of a new Hibernate session which shares the same transactional (JDBC Connection) resource as the currently bound session.
However, several comments by Graeme and observed behavior show that it is now opening a new connection by design:
https://github.com/grails/grails-core/issues/10599#issuecomment-296542627 https://github.com/grails/grails-core/issues/10503#issuecomment-283313398
It used to be the case that you needed to use withNewSession to isolate certain updates/flushes from the outer session. For instance, it was common to use it in a custom validator to prevent polluting or accidentally flushing the outer session. It was also common to use it in the before* type callback methods, if querying data, to prevent accidental flushes.
Some of that is remedied by using FlushMode.COMMIT as the default, but it still seems like withNewSession can't really be used that way at all anymore.
If it starts a new connection, then by definition it can't share the transactional context (presuming no XA or anything as far as I know), so the context is completely different.
I'm struggling to understand how this was a "bug in withNewSession" and not a fundamental change in what and how this method is used.
At a minimum, the documentation needs to change to reflect the change in behavior and possibly give some references to how or why it should be used.
The documentation is incorrect. Spring removed the ability to do what is described in the documentation in newer versions. The current version of SessionHolder supports only a single session:
https://github.com/spring-projects/spring-framework/blob/master/spring-orm/src/main/java/org/springframework/orm/hibernate5/SessionHolder.java
Previous versions allowed multiple coordinated sessions https://github.com/spring-projects/spring-framework/blob/4.0.x/spring-orm/src/main/java/org/springframework/orm/hibernate3/SessionHolder.java#L49
Since this behaviour is gone in Spring, there is nothing we can do to bring it back and had to implement it differently in newer versions.
I agree it is a fundamental change, but when the underlying frameworks remove the ability to do something there is nothing much that can be done.
The documentation needs correctly however.
Makes sense. I guess my issue is that I’m not sure how to replace it for some of the common scenarios before.
For instance, what if you need to fetch or even update data inside a beforeInsert callback? Before, withNewSession was useful but in this form it doesn’t really work well for that purpose since it can’t see data in that transaction and can’t participate in it either. Same for custom validators that need to fetch other data.
Maybe the answer is just “don’t do that” and move that logic out into a service, which is fine. Just wanting to make sure there’s not a simpler replacement before refactoring a bunch of stuff.
If you have flush.mode set to COMMIT (which is recommended) then you can query using the current session in a beforeInsert since flushes only occur when data is committed and not for each query.
By using withNewSession it was possible to use custom validation which was fetching same data from database, which were already associated with previous session but in additional it was possible to access previously flushed data but not yet committed in active transaction. Because of those changes in Spring and Grails dependency on this particular changed implementation it is not possible to use this approach anymore. We are using flush.mode MANUAL so for us there is no problem with automatic flushing. But we will have problem "A different object with the same identifier value was already associated with the session..."
I am afraid that we have to rewrite some validations so they will be not using withNewSession at all. And in additional all returned object used in those validations must be written in way, that they will not return any reference to whole domains but just their IDs. So probably it would mean to build some additional structure which will mimic existing domains relations but just holding important properties for validation and just using ID as reference to another domain.
Or is there any other way how to "easily" achieve similar behavior?
By using withNewSession it was possible to use custom validation which was fetching same data from database, which were already associated with previous session but in additional it was possible to access previously flushed data but not yet committed in active transaction. Because of those changes in Spring and Grails dependency on this particular changed implementation it is not possible to use this approach anymore. We are using flush.mode MANUAL so for us there is no problem with automatic flushing. But we will have problem "A different object with the same identifier value was already associated with the session..."
I am afraid that we have to rewrite some validations so they will be not using withNewSession at all. And in additional all returned object used in those validations must be written in way, that they will not return any reference to whole domains but just their IDs. So probably it would mean to build some additional structure which will mimic existing domains relations but just holding important properties for validation and just using ID as reference to another domain.
Or is there any other way how to "easily" achieve similar behavior?
We had same kind of issues! With Grails 2 we used withNewSession in custom validators. With Grails 3 we just removed the withNewSession closures from each domain with such a custom validator. It seems to work OK!