Type Safe Criteria API for Hibernate
Hibernate is one of the broadest deployed libraries in the Java world. It has powerful techniques for mapping objects to a relational database, and rich, vendor agnostic query facilities to make it easy locate objects. One of the APIs for queries, the Criteria API, is useful if you need to dynamically compose a query (from a search form, for instance). However, one of the things that has always bothered me about the Criteria API is it's broad use of strings for property names. It is a source for error that doesn't show up until after an application is deployed and used. It is a good idea to have errors show up as early as possible, so, ideally, I want editor and compiler support for checking my queries just like it checks my code. SafeCriteria does this by allowing you to specify properties via the methods that retrieve those properties which can be checked by the compiler, rather than at runtime when Hibernate tries to evaluate the query.
Here's how this library can help you. Let's take for instance one of the queries used in the Hibernate documentation for Criteria.
List cats = sess.createCriteria(Cat.class) .add( Restrictions.like("name", "Fritz%") ) .add( Restrictions.between("weight", minWeight, maxWeight) ) .list();
As you can see, there are a couple of restrictions here that are limiting the query to return Cat's with names that start with "Fritz" and have a weight between some minWeight and maxWeight. This is a fairly readable query, but one thing that sticks out a bit in the use of strings for both the property names we want to filter on. It's not such a big deal in a single, small query, but if you write a lot of these or have a large number of criterion in your query this can become a source for problems.
Using SafeCriteria, you can rewrite the query as follows.
List cats = SafeCriteriaFunctions.from(sess, Cat.class) .add( SafeRestrictions.eq(SafeCriteriaFunctions.of(Cat.class).getName(), "Fritz%") ) .add( SafeRestrictions.between(SafeCriteriaFunctions.of(Cat.class).getWeight(), minWeight, maxWeight) ) .list();
The nice thing about the second query is that we don't specify any property names for the restrictions. However, the query is much larger than it was before. However, if you use static imports, you can make the query much less verbose.
import static net.grogscave.safecriteria.SafeCriteriaFunctions.*; import static net.grogscave.safecriteria.SafeRestrictions.*; ... List cats = from(sess, Cat.class) .add( eq(of(Cat.class).getName(), "Fritz%") ) .add( between(of(Cat.class).getWeight(), minWeight, maxWeight) ) .list();
The SafeCriteria also adds some nice syntactic sugar to make your queries more like traditional SQL/HQL so you could write the above query like the following.
import static net.grogscave.safecriteria.SafeCriteriaFunctions.*; import static net.grogscave.safecriteria.SafeRestrictions.*; ... List cats = from(sess, Cat.class) .where( property(of(Cat.class).getName()).like("Fritz%") ) .where( property(of(Cat.class).getName()).between(minWeight, maxWeight) ) .list();
This library is a work in progress, so feedback is always welcomed!