views:

593

answers:

3

Hi,

I'm currently working on a (rather large) pet project of mine , a Swing application that by it's very nature needs to be multi-threaded. Almost all user interactions might fetch data from some remote servers over the internet , since I neither control these servers nor the internet itself, long response times are thus inevitable. A Swing UI obviously cannot repaint itself while the EDT is busy so all remote server calls need to be executed by background thread(s).

My problem:

Data fetched by the background threads gets 'enriched' with data from a local (in-memory) database (remote server returns IDs/references to data in the local database). This data later eventually gets passed to the EDT where it becomes part of the view model. Some entities are not completely initialized at this point (lazy-fetching enabled) so the user might trigger lazy-fetching by e.g. scrolling in a JTable. Since the hibernate session is already closed this will trigger a LazyInitializationException. I can't know when lazy-fetching might be triggered by the user so creating a session on demand/attaching the detached object will not work here.

I 'solved' this problem by:

  • using a single (synchronized , since Session instances are not thread-safe) Session for the whole application
  • disabling lazy-fetching completely

While this works, the application's performance has suffered greatly (sometimes being close to unusable). The slowdown is mainly caused by the large number of objects that are now fetched by each query.

I'm currently thinking about changing the application's design to 'Session-per-thread' and migrating all entities fetched by non-EDT threads to the EDT thread's Session (similar to this posting on the Hibernate forums).

Side-note: Any problems related to database updates do not apply since all database entities are read-only (reference data).

Any other ideas on how to use Hibernate with lazy-loading in this scenario ?

A: 

Don't expose the Session itself in your data API. You can still do it lazily, just make sure that the hydration is being done from the 'data' thread each time. You could use a block (runnable or some kind of command class is probably the best Java can do for you here unfortunately) that's wrapped by code that performs the load async from the 'data' thread. When you're in UI code, (on the UI thread of course) field some kind of a 'data is ready' event that is posted by the data service. You can then get the data from the event use in the UI.

arcticpenguin
Maybe I didn't understand your answer correctly (English not being my first language) but wouldn't this still trigger a LazyInitException when the view model (=EDT) tries to lazily fetch data (since EDT != 'data' thread) ? Anyway, your suggestion would work for my application if parsing the server's response would be performed on the EDT and not by some other thread as it's currently being done....hmmmm...I have to think about this ;) Thanks for taking your time !
JavaGuy
His answer presumes you have a single session in the data thread which you do not close. However this suffers from the problem of the ever growing L1 cache unless you're very fastidious about detaching items from the session when they're no longer needed.
Jherico
I'm aware of the "ever growing session" problem but currently this is currently far outweighted by the 'lazy fetching disabled' problem ;) Wouldn't I still need two Sessions for this (one for the data thread, one for the EDT) so that lazy-fetching within the view works ?
JavaGuy
A: 

There are two distinct problems, that should get resolved seperately:

  1. Handling of Hibernate Sessions in Swing Applications. Let me recommend my own article, regarding this problem: http://blog.schauderhaft.de/2008/09/28/hibernate-sessions-in-two-tier-rich-client-applications/

The basic idea is to have a session for every frame, excluding modal frames which use the session of the spawning frame. It is not easy but it works. Meaning, you won't get any LLEs anymore.

  1. How to get your GUI thread separated from the back end.

I recommend to keep the hibernate objects strictly on the back end thread they originate from. Only give wrapper objects to the ETD. If these wrapper objects are asked for a value, they create a request which gets passed to the backend thread, which eventually will return the value.

I'd envision three kinds of wrapper Implementations:

Async: requests the value, and gets notified when the value is available. It would return immediately with some dummy value. On notification it will fire a PropertyChange event i.O. to inform the GUI about the 'changed' value (changed from unknown to a real value).

Sync: requests the value and waits for it to be available.

Timed: a mixture between the two, waiting for a short time (0.01) seconds, before returning. This would avoid plenty change events, compared to the async version.

As a basis for these wrappers a recommend the ValueModel of the JGoodies Binding library: http://www.jgoodies.com/downloads/libraries.html

Obviously You need to take care that any action is only performed on actually loaded values, but since you don't plan on doing updates this shouldn't be to much of an issue.

Let me end with a warning: I have thought about it a lot, but never actually tried it, so move with care.

Jens Schauder
won't work for shared references, but it would in copied references
arcticpenguin
I am not sure what you mean by shared references. But I probably agree :) Hibernate objects and their sessions need to get confined to a well defined scope, and the scope of two sessions must not overlap. If you need to move an object from one scope to the other, you'll have to reload it in the target session.
Jens Schauder
A: 

You could look have a look at Ebean ORM. It is session-less and lazy loading just works. This doesn't answer your question but really proposes an alternative.

I know Ebean has built in support for asynchronous query execution which may also be interesting for your scenario.

Maybe worth a look.

  • Rob.
Rob
Hi Rob,Thanks for the link - I assume you're the lead developer ? ;-) Anyway, sounds promising ... I think I'll give it a try when I have some time to spare.
JavaGuy