Models Inc.

Note: This section will soon be updated for non-Hibernate projects, though many of the same concepts apply.

The heart of Databinder is the HibernateObjectModel. It’s where Hibernate meets Wicket to transparently load and persist data reflected in the user interface. Understanding how it works will put you on your way to smooth Databinder programming.

IModel

Before delving into HibernateObjectModel’s depths, let’s go over the IModel interface it implements.

As is the trend in GUI frameworks (e.g. Cocoa Bindings ), Wicket binds UI components formally to the data they represent. You might think that a component could reference a plain object as its model and be done with it, but there are several ways the IModel middle-man earns its commission.

Firstly, it allows the actual model object to be an imaginary projection. Instead of assigning an object to all child components, you often benefit from a CompoundPropertyModel assigned to a root component. This root model handles model requests for its child components with no models. Their wicket:id values map to properties of the root model object implicitly.

This same idea can be taken in unusual directions, like our SublistProjectionModel. It’s also common to have anonymous-subclass models that combine or operate on data to build model objects on the spot. These things wouldn’t be possible without a model container standing between components and their model objects.

The other purpose of IModel is detachability. We can’t always toss plain objects between Web requests. Some objects are just too big to be retained for each user of a busy Web application. And some objects—all Hibernate managed objects—are associated with particular database sessions and need help to go between two. Custom IModel implementations, such as Databinder’s, handle persistence of component data, allowing Wicket component classes to remain storage-generic.

Hibernate

We like Hibernate because it factors out a significant redundancy: the code that shuttles data between rows in the database and objects in memory. Instead of writing those instructions for every class or littering the source tree with generated JDBC code, Hibernate performs these operations internally. It’s a mature, widely-used technology written by people who know far more about database programming than most of us.

Even so, using Hibernate is not without costs. Some mapping relationships, like all-delete-orphan collections or joined subclasses, can be a real brain-teasers. But let’s be fair: those are difficult problems no matter how you approach them. Who even attempts full-featured collections and class hierarchies in JDBC? Before Hibernate and other relation mapping tools, most people made do with very limited object orientation in their persisted objects.

Skimming through the Hibernate documentation is like walking though an ORM candy store. Almost anything is possible, and looks easy. There aren’t any warning signs when you go from mapping simple properties to collections. So consider this a sign: don’t even think of using the more complicated constructions until you’ve experimented with the simpler ones. You will only be wasting your time solving problems that you could tackle later in half the time.

After the burden of learning relational-mapping concepts and terminology, Hibernate’s biggest cost is session babysitting. Though entities created by Hibernate look and feel like normal objects, the illusion only holds to a single “unit of work” session. When that session is closed, you can no longer trust those entities to even display correctly, as parts of them may be lazy loaded. Trying to work around the restriction is futile.

This presents an immediate problem, as the open session in view technique is by far the most common for Web applications. A “view” of one request cycle does not necessarily correspond to a practical “unit of work,” which often spans several requests. And in Wicket, one of the greatest pleasures is ignoring the request cycle so that Web interface logic can resemble desktop GUI logic. The disconnect between this mode of interaction and a single-request Hibernate session would seem to be devastating.

HibernateObjectModel

Happily, IModel’s detach logic provides an answer to the problem of carrying Hibernate entities between web requests. If we discard our references to the entities before the session ends, and reload them before use in a new session, it’s almost like we had the same object all along.

Almost. Any changes that weren’t persisted to the database will be undone. The programmer must either commit changes (as done by default in DataForm.onSubmit()) or let them evaporate. This isn’t so bad really, compared to practically every Web application design, but it is a little grating. Databinder does include experimental support for Hibernate sessions that span requests, but for most applications it’s better to think in terms of committing changes at every request.

With that caveat behind us, HibernateObjectModel acts as our trusted entity transporter. As long as any entity loaded by Hibernate goes into one of these, we can use it without fear for the next one thousand requests. It can travel between components, and even between pages. It is the session babysitter.

Page last updated May 10, 2008. Produced with Databinder.