Databinder, we need to talk
It’s been a while since I’ve seen Databinder this excited about anything. The last time was probably its film debut. The big story today is that extended sessions for long conversations actually work. We tried them.
Keeping open a Hibernate session between Web requests is a crazy idea. It reminds me of the way people used to still complain about Wicket’s use of the Web session for something besides a 32-bit integer representing a user key. You’ll run out memory. That doesn’t scale. The enterprise will fire proton javabean torpedos into your despicable flouting of convention!
Anyway, who cares about those boring people. Chuck (on the Databinder forum) has been working on a use of ajax that constantly butts up against stale object exceptions. It took me a long time to understand his app’s problem. And then I forgot what it was and had to have it explained again.
The gist of it is, a page with ajax controls can operate a lot like a multi-page transactional “wizard.” Each time the user acts, a request goes to the server that alters the model. No problem there, except when you don’t want to commit yet. The user should be able to save or abandon his changes at any time.
Using detachable Wicket models with a standard Hibernate session does not offer a solution to this problem other than 1) just shut up and persist those changes, or 2) store those changes in some object that hasn’t yet been persisted. I tried to sell Chuck on each of those and he was appropriately unimpressed.
Enter white horse, conversational rider
The correct answer to the problem is to use a conversational session. Everything can work normally in such a session, you can even detach models, and the changes are held until you flush or clear them.
I experimented with a number of different methods for holding this session before deciding on the page object itself. This maps the functionality to the user’s expectations (back, forward, multi-window browser use) better than tying a single Hibernate session to the Web session. Wicket manages the page map, so all Databinder has to do is check for sessions in the pages before loading any objects.
Easier said than done, of course. I’m not sure exactly what’s coming down the Wicket 1.3 pike in the way of “tightening” request callbacks, but for now I’ve had to resort to a hack that hooks into request target resolution. It mostly works.
When the conversation functionality is enabled for an application, Databinder inspects the request target as soon as it’s available and binds the page’s session, if present, to the thread. When there is no session but the page is able to hold one, the new session is placed in the page and marked for “manual” flush. Again at the end of the request Databinder assesses the responding page. If it can hold a session and the session is dirty, the session is left open (though its JDBC connection is closed) and placed in that page. Clean sessions are closed and nullified.
In the ideal scenario users follow tasks to their completion where the session is either flushed or cleared, causing Databinder to neatly dispose of it. But in the obvious scenarios where conversational sessions are left open and abandoned, they fall off the end of the page map and are garbage collected without error.
Phone directory conversates
Now, the fun stuff. I decided to apply Chuck’s style of interaction to Databinder’s classic phone directory example. Instead of editing the entries in a box floating to the right of the listing, the new version [Update: no longer online, but it will be behind the first link when Databinder 1.1 is final] presents them in an ajax modal dialog. You can make as many changes as you want, and then save or revert them all at once. (With the exception of adding, which flushes automatically because of the generated key.) The source is available if you can’t believe your eyes.
Oh, and watch out for optimistic locking exceptions.
Codercomments
Many months on are you still a fan of this approach? Have you any “top tips” for other people doing something similar?
I think we are in a similar position of wanting to manipulate a complex object graph managed by hibernate via Wicket…
Cheers
Sam
I’m glad I tried it, just to test out the theory, but for my own Hibernate apps I still use request-length sessions. It seems too risky, and without much reward, to aggressively employ a technology that has basically been shelved. (Who is using anything but session-in-view with Hibernate, other than a few desktop application developers?) There’s surely a lot of undiscovered kinks to work through in making it work solidly on the web, and someone more motivated to use the technology than myself would have to work them out.
Although, perhaps you may want to take it on. I’ve put Databinder into Git so that it takes all of five minutes for anyone to branch it. Or for what you’re describing, if the app does not exist yet, I would seriously consider Cayenne. Even though I haven’t used it much beyond a demo app, it seems very well suited to complex object graphs. And, it doesn’t complain when you take an object between two sessions, so you don’t even have the problem described (afaik).
Add a comment