When evicting (either directly or by cascade) a proxy from a session, the proxy is removed from an internal map (proxiesByKey), but the proxy's lazy initializer still has reference to the session. This allows for lazy initialization of a proxy even after the proxy is evicted. This seems semantically incorrect: The documentation of LazyInitializer.getSession() reads: "Get the session, if this proxy is attached" an proxy evicted from a session should cleary not remain attached to the session.
The current implementation leads to concurrent modification exceptions, when one thread detaches entities from one session and another attaches these entities to another. The method AbstractLazyInitializer.isConnectedToSession() is called when locking a proxy to a session. This will search the old session for the proxy (in the containsProxy() call). When another thread is manipulating this sessions proxy ByLey map, a concurrent modification exception will occure.
Note that each session is used in a thread-safe context by the application! When reassociating a proxy to the new session, hibernate breaks the thread-safety by querying the other session.
The attached testcase shows the behaviour. It also shows the difference in behaviour when evicting a lazy collection or a proxy.
This one-line fix sets the session of a proxy to NULL when evicting. This solves the problem and all other tests run fine.