dagblog.
← Archive

Legacy databases and ORM tools


It is fairly straightforward to use ORM tools such as Hibernate or other JPA implementations when we have full control of the database schema. An ORM tool really proves its worth when we have an existing schema we can barely change, perhaps because we have to keep it backward compatible. That is exactly the job I am facing, and I would like to share some observations.

A rich object model

Most of the time, the object model we are trying to map is much richer than the database model. A class can contain other embedded classes mapped within the same table or a joined one. In this case we map what Hibernate calls components. A component lets us handle a case like this one.

We have tables called Osoby (People) and Domy (Houses). Both contain the columns mesto, ulice and psc (city, street and postal code). We create an Adresa (Address) object holding those three values. We can map Adresa independently as a component in both Osoby and Domy.

A component can also handle the case where Adresa has its own table but we do not want to map the Adresa class as an entity. In the Osoby and Domy mappings we join the address table and map Adresa itself as a component again.

A custom UserType converter

In an existing database schema you are bound to find column types the ORM tool cannot convert to the appropriate Java type. Perhaps a VARCHAR column contains 0 and 1 and needs to map to a Java boolean. Or perhaps the database stores a date as a number, but your Java POJO has an accessor for java.util.Date.

A custom value type is intended for exactly these conversions. Implement the org.hibernate.UserType interface, declare it in the mapping and use it for the appropriate property. Here is an example converting the strings 0 and 1 to a boolean.

public class ZeroOneType extends org.hibernate.type.CharBooleanType{
protected String getFalseString() {        
return "0";
}
protected String getTrueString() {        
return "1";
}
public String getName() {
return "0_1";
}
}
<class...
For this converter we do not even need to implement org.hibernate.UserType directly. Gavin King and his crew thought of us and provided the abstract class org.hibernate.type.CharBooleanType to map boolean values to strings and back.

Hibernate, annotations vs. XML, and more

I could go on about other handy tools, such as mapping the same class more than once, strategy objects for setting Java properties or tuplizers, but these are extensions you only get with Hibernate. Two or two and a half years ago I would have had serious reservations about using ORM on an existing database schema. Given what Hibernate can do today, I would not hesitate.
These extensions may come in handy even if you have full control of the database schema and can change it at will. They make a substantial difference between plain JPA and Hibernate. For me, that is one more reason to use Hibernate's capabilities to keep control of the whole mapping instead of relying on JPA.
Annotations vs. XML
If you use an ORM tool, you may wonder whether to use annotations or XML. Many developers buried XML descriptors when annotations arrived, but that is the wrong approach. For mapping legacy data, I found XML more suitable. Annotations make sense where they express metadata more simply than XML.
Annotations look very appealing for what you might call simpler mapping cases. But if you have ever seen a class with somewhat more complicated annotations, for instance around database types, you know how quickly their readability disappears. Remember that a class can contain more than just mapping annotations, and suddenly we are in annotation hell.
I have another issue with mapping annotations, purely architectural. They make the class reveal things that the client should be shielded from. For example, they show how the class is stored in the database and that it depends directly on something like the JPA API. A bytecode dependency means an integration problem when you give someone your domain model classes.

Conclusion

Mapping legacy data looks like a solvable problem with Hibernate's capabilities. I am still near the start of solving my own problem, which goes well beyond legacy data. Hibernate looks flexible enough to handle it and still offers considerable value over plain JDBC.

Medium version

Related articles

Related CZ Podcast episodes