How Plain Are Your POJOs?
Back in the old days, when we wanted to write our model objects to the database, we put a bunch of CRUD methods in the classes to make JDBC calls to do what we wanted. This did its job and was easy, if a bit tedious, to program.
We refactored a bit over the years and got a lot of the CRUD out of model objects, but we still had to implement some interface, or rely on slow reflection code to map things to the database.
Then Hibernate came along and finally our model objects didn’t even need to know there was such a thing as a database. Pure, clean, model objects that had data in when we needed it and from which changed data magically reappeared in the database. Nice. We achieved our goal of having model objects just being model objects, POJOs, and abstracted the database handing away to a place we didn’t even know existed in five years ago.
And then we had Spring. Nice clean classes, dependencies being injected, the whole program elegantly concentrating on business logic instead of being littered with, well, whatever it was we used to have to hold our programs together. Suddenly whole new areas of code could be POJOs as well.
All very nice. But I was uneasy. Certainly my classes looked plain, but actually there was a finely balanced symbiosis between Spring and Hibernate and my objects. Yes they were plain old Java objects, but they had to be a very specific type of “plain”. So not really that plain at all, in fact, I couldn’t help thinking, shouldn’t this level of plainness be enforced by some sort of interface?
Well no. This sort of “specific plainness” couldn’t be enforced by an interface. It was in fact, closer to the duck-typing that the Ruby guys make such a racket about. Hibernate and Spring expected the methods to be there and if they were (and did the right thing), all would work, otherwise it wouldn’t.
And that’s where I expected this post to end when I first thought about writing it a couple of months ago. But recently, there have been some developments…
I’ve started using Hibernate 3.2 and JPA with Annotations. Suddenly my not-so-plain-POJO’s are full of javax.persistence and org.springframework imports, and annotations litter the code. It’s as if I’ve refocused my eyes, and the hidden structure imposed by the magic of Spring and Hibernate has materialised into the code.
So is this a good thing? I can declare my setters as @Required to make sure Spring’s config is doing its job, and the Hibernate config has gone away, it’s all in the annotations. So no need for a careful balance between a config file and the code. The various players are still co-dependent, but it’s now a visible and robust symbiosis which will quickly let you know if something’s been upset.
This new found syntactic crud in my code is a bit untidy, but syntax highlighting can help with that, and while I still miss my POJOs, actually deep-down I know they were never that plain.
There are some contentious issues; I think we need to ensure that the things we annotate in our classes are genuinely meta-data about that class rather than specifics of a particular framework. @Required is a Spring annotation, but if a value is required, it’s required regardless of Spring. The maximum length of a String doesn’t change whether we use JPA or some other persistence mechanism. Which things are conceptually “valid” for annotating may be a bit of an area to think about. For example, I don’t think it’s valid to be annotating the Oracle tablespace in which a particular table lives, or the name of a database constraint – so perhaps it’s not valid to be storing table names and column names in the model objects? I don’t know, perhaps we trade some purity of intent for convenience and clarity?
On the whole, I think the new way of doing things is good, and will reduce maintenance overhead by reducing the chance of errors; and where failures occur things will fail early and tell us why.
Annotations don’t complicate the main Java code, but clearly express the contract between your POJO and the hidden forces which work upon it. The benefit of a compiled, typed language is the amount of validation which can take place at compile time (which otherwise you’d have to build into your unit tests anyway), annotations allow us to take that a bit further.
We’re getting closer to methods being able to declare a complete contract for their usage – which would make debugging and maintenance of code much, much easier.