views:

135

answers:

2

I'm struggling with real-world use of JPA (Hibernate, EclipseLink, etc) in a Swing desktop application.

JPA seems like a great idea, but relies on lazy loading for efficiency. Lazy loading requires the entity manager exist for the lifetime of the entity beans, and offers no control over what thread is used for loading or any way to do the loading in the background while the EDT gets on with other things. Accessing a property that happens to be lazily loaded on the EDT will block your app's UI on database access, without even the opportunity to set a busy cursor. If the app is running on wifi/3G or slow Internet that can make it look like it has crashed.

To avoid lazy loading stalling the EDT I have to work with detached entities. Then, if I actually need the value of a lazy property all my components (even those that should supposedly be able to be unaware of the database) have to be prepared to handle lazy loading exceptions or use PersistenceUtil to test for property state. They have to dispatch entities back to the database worker thread to be merged and have properties loaded before being detached and returned again.

To make that efficient, my components need to know in advance what properties of a bean will be required.

So, you'll see all these shiny tutorials demonstrating how to whip up a simple CRUD app on the NetBeans Platform, Eclipse RCP, Swing App Framework, etc using JPA, but in reality the approaches demonstrated violate basic Swing practices (don't block the EDT) and are completely non-viable in the real world.

( More detail in write-up here: http://soapyfrogs.blogspot.com/2010/07/jpa-and-hibernateeclipselinkopenjpaetc.html )

There are some related questions with somewhat helpful responses, but none of them really cover the edt blocking / lazy loading / entity manager lifetime management issues together.

http://stackoverflow.com/questions/1778578/lazy-eager-loading-strategies-in-remoting-cases-jpa

How are others solving this? Am I barking the wrong tree by trying to use JPA in a desktop app? Or are there obvious solutions I'm missing? How are you avoiding blocking the EDT and keeping your app responsive while using JPA for transparent database access?

+1  A: 

I've only used JPA with an embedded database, where latency on the EDT wasn't a problem. In a JDBC context, I've used SwingWorker to handle background processing with GUI notification. I haven't tried it with JPA, but here's a trivial JDBC example.

Addendum: Thanks to @Ash for mentioning this SwingWorkerbug. A workaround is to build from source has been submitted.

trashgod
For reference, the version of SwingWorker in the Sun/Oracle JVMs from JDK6u17 on apparently has a pretty major bug: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6880336. There are some workarounds at the end of the comments.
Ash
Actually doing the background processing is fuss free. The trouble arises, with JPA, because you don't know *when* that background processing is required, as it's all "transparent" with lazy-loading via cglib proxies.When you're working with database-aware code and explicit background loading it's easy to know when to call into your background database worker with a callback Runnable, use SwingWorker, or whatever.With JPA, though, you don't know you need to load something until the UI is already blocked waiting for it to load.
Craig Ringer
Re the SwingWorker bug: I typically use the Executor system directly anyway, with a background database worker processing runnables with on-completion callbacks. But thanks for the tip.
Craig Ringer
@Ash: Thank you!
trashgod
+2  A: 

I have encountered the same problem. My solution was to disable lazy loading and ensure that all entities are fully initialised before they are returned from the database layer. The implications of this is that you need to carefully design your entities so that they can be loaded in chunks. You have to limit the number of x-to-many associations, otherwise you end up retrieving half the database on every fetch.

I do not know if this is the best solution but it does work. JPA has been designed primarily for a request-response stateless app. It is still very useful in a stateful Swing app - it makes your program portable to multiple databases and saves a lot of boilerplate code. However, you have to be much more careful using it in that environment.

Russ Hayward
Unfortunately, not using some form of lazy loading isn't viable in this environment. I have 10,000 customers with hundreds of thousands of related entities in the database. Some entities necessarily have large or complex data associated with them that's required by some parts of the app, but not most.This article covers some of the design issues with using JPA / Hibernate / etc in a 2-tier desktop app: http://blog.schauderhaft.de/2008/09/28/hibernate-sessions-in-two-tier-rich-client-applications/ ... and concludes that, really, there just aren't any good solutions. I'm inclined to agree.
Craig Ringer
In that case the only thing I can suggest is that you build a layer on top of your entities that uses an asynchronous mechanism to gain access to lazy loaded properties. So you would have something like a get method but you would pass an interface to it that would get notified of the response when it is ready. If you only used this top layer from your GUI it would prevent EDT blocking and avoid your GUI from having to deal with the messy property state checking and merging process. However, it would probably require a significant rewrite of the GUI.
Russ Hayward