First off, I've perused the forums searching for related posts. There are many, this being one of the longest:
http://forum.hibernate.org/viewtopic.php?t=925435
I realize that the standard answer to this question is: Don't do it.
Indeed, I agree with the reasoning for this recommendation, and would never even think of synchronizing a session in a web application, for scalability reasons and all the other reasons mentioned in the thread above:
My reasons for wishing to do this are best summarized here:
http://www.hibernate.org/333.html
I'm working with a Desktop application running on top of an embedded Java database (Derby, H2, etc). Thus scalability is a non-issue. There is only one user. However, there may be multiple threads. There is the Swing Thread, and there are Worker Threads. All of these may access the DB, and thus need a Session. So there are two options: ThreadLocal Sessions or serialized, synchronized Session access.
So I tried ThreadLocal and immediately hit problems when trying to re-attach detached objects to these ThreadLocal sessions. Since normal practice for Desktop apps is to keep one reference to in-memory objects, we always just reattach them to sessions before performing work on them. The problem arises when two threads both try to reattach the same object (and its transitively persisted components). In this case, an 'illegal attempt to attach a collection to two open sessions' error results. Well, that's pretty clear: It's not allowed to attach the same in-memory object to two different sessions. Reloading from the database isn't an option, since the in-memory object has been changed since it was last loaded into the DB.
To solve this, it would be necessary to make sure no other sessions had the same collection already attached. I can't think of any sane way of doing this that wouldn't still leave at least a small window of concurrent failure possible. Unacceptable, since this would crash the app.
Bring me back to the other option: Make sure only a single Session is in use at any given time (though this single reference will still be regularly flushed, closed, and recreated).
So how does one synchronize (on) the Session without placing undue burden on application developers? The first post mentioned above contains the (paraphrased) advice: Create a proxy for the session.
I understand this to mean: Wrap the Session in a proxy object that delegates method calls to the real session, synchronizing those method calls. This sounds good (in fact, I even tried it once), but I think it still leaves at least one loophole open: lazy loading.
Even if a I synchronize all calls to the individual session methods, object proxies in the session (lazily-loaded) may still hydrate themselves via the real session, potentially in different threads. There may be other contingencies I haven't yet encountered/thought of.
Is there any 'elegant' way to synchronize the session (make it thread safe) that doesn't put much of the burden on clients of the session? If I proxy the Session, what else what still need to be done?
Basically I'm trying to avoid having synchronize{} blocks peppered everywhere in the app-level code, which is not only ugly, it's easy to get wrong, especially with multiple coders on the project, and literally hundreds of places in code that perform database access.
Note that we don't rely heavily on lazy instantiation. However, we do rely on lazy loading to speed querying to get and display basic information, the objects returned by such queries are usually discarded. In cases where we intend to keep loaded objects in-memory, we practice full initialization by traversing the object graph. This makes in-memory objects safe to traverse once the session has closed.
So, sorry that was so verbose, but figured it was necessary if I expected to get any answer other than 'Don't do it'
Would love to get ThreadLocal sessions to work for us, but I fear it's even thornier than a synchronized session.
Note that we are currently using a synchronized approach currently, but this is achieved by use of synchronize{} blocks around all our access code , which is quite tedious, ugly and error-prone. But it works. Very well.
So I'm trying to find a way to clean up our approach to encapsulate some of the grunt work required in wrapping everything in synchronize blocks.
So, if you're still reading, thanks for that. If you have some advice, that would be even better!