In a previous post I lamented about Tapestry and Hibernate fighting each other. This was very soon after I had started using Tapestry for the first time, and I wanted to talk a little bit more about it now that I’ve been using it for a while. So now I can rant with much more detail.
Cut to the Chase
The short answer is that I have more of a mixed feeling about it now than I did before, but that I still think that Tapestry and Hibernate are not a good solution. There is too much of an impedance mismatch between the two. They are both very complex frameworks with leaky abstractions that make you have to know too much to use them well.
Complexity and Leaky Abstractions Sink Ships
Hibernate and Tapestry are incredibly complex pieces of software. Throw in a lot of hidden, “magic” functionality in Tapestry from its use of Hivemind and it’s even harder to understand what’s going on. This complexity and lack of transparency means that understanding the deterministic behavior of an application is incredibly difficult. It seems like people “just try different things until something works”. I would call this Programming by Coincidence. The abstractions are just leaky enough that you have to know about the implementation details to effectively use the framework.
The @Persist annotation (or the persist property in the jwc/page files, etc) store objects in the Web Session. If you modify the state of the object that change is not persisted because it does not know the object was changed. You have to actually call the setter of the persist property to get a new value set. This doesn’t sound like that big of a deal until you try to integrate something like Tapernate to help with Hibernate persistence issues. Tapernate will merge the persistent Hibernate object back into the current Hibernate Session. Well, if you’ve modified an @Persist bean and Tapernate merges it back into the Hibernate Session, you will end up with stale data in your Hibernate session unless you call the setter and set the value to null. Well if you set the value to null, then you break the back button because someone will click back and then get a NullPointerException if they try and submit the form.
I should not put all of the blame on Tapestry though. Hibernate is itself a hugely complex piece of software as well. (ORM is a complex problem, so some of this complexity might be unavoidable.) There are so many possible ways of mapping objects that you can never truly know if you’ve done it the right or the best way, and best is always dependent on your situation and your application uses which makes it even harder to discuss with others whether it’s best. Hibernate does a lot of work to ensure the consistency of it’s Session, if there’s a possibility of it being inconsistent, it throws exceptions.
So you get LazyInstantiationExceptions because the Session is closed, you get NonUniqueObject exceptions because an object with the same id is already in the Session, you get TransientObjectExceptions because you didn’t schedule a new Object for saving in the session, etc, etc. Way too much implementation detail! Of course if you ever suggest to the Hibernate people that this is too much detail and they “should just handle it”, you get told how smart they are and how dumb you are and how you wouldn’t like the consequences of them not throwing these Exceptions. Isn’t that helpful.
As Ted Neward said, ORM is The Vietnam of Computer Science
This can’t be the best we can do. We’ve seen interesting things like Active Record come out of the Ruby world. Not that Active Record is perfect, but I have yet to see a NonUniqueObject exception or a Lazy Loading error which is a Good Thing ™ since I don’t care about those details. I think the Application centric, declarative configuration of Spring is much easier to understand and less error prone than the configuration of Hivemind.
All things being equal, Using fewer frameworks is better. Using less complex frameworks is better. Managing complexity is the premier goal of software development. Code reuse should not trump simplicty of design and simplicity of implementation.