I’m now working on my second project with Hibernate, having delivered the first, and I’m playing with some of the features I didn’t have time to figure out last time. And I have to say: named queries rock, big time.
Our datamapper layer has one method for loading objects. It takes a query name and a map of properties (for name-value pairs). We create the query by name and then copy the map into the query, like so:
// Exception handling removed for brevity. public List find(String queryName, Map params) { Session session = _sessionFactory.openSession(); try { Query query = session.getNamedQuery(queryName); for (Iterator i = params.entrySet().iterator(); i.hasNext(); ) { Map.Entry entry = (Map.Entry) i.next(); query.setParameter((String) entry.getKey(), entry.getValue()); } return query.list(); } finally { session.close(); } }
(Another option would have been to pass an object in, and simply pass that in and use JavaBean getters to copy parameter values in. But the map looks easy to set up from the caller’s side)
We then define the queries using XDoclet tags in the domain class, along with all of the other Hibernate tags. This gives us a very nice grouping of the queries, and we can easily see what queries have been written for an object (kind of like EJB finders, but without the compile time linkage). That’s important for indexing the database later.
This just makes whipping out new queries so bloody easy. Define a test for the query, see it fail, define the HQSL, run the test, see it pass, call it in the client (insert suitable tests here), and away you go.
If you use Hibernate, and you’re not using named queries, what are you waiting for?
Updated:
By request, here’s an example. Start with the test.
/** Find accounts by borrower name. */ public void testFindAccountsByBorrowerName() { Map parameters = new HashMap(); parameters.put("borrowerName", TestFixture.ACC_NAME); DataMapper mapper = new HibernateDataMapper(); List results = mapper.find("findAccountByBorrowerName", parameters); assertNotNull(results); assertFalse(results.isEmpty()); assertTrue(results.contains(TestFixture.ACCOUNT)); // TODO: Maybe check the other results to ensure that we haven't brought back any // false positives }
Client code looks similar; construct a map of parameters somehow, and pass it through.
The XDoclet annotations get a little more interesting. For full details, refer both to the Hibernate manual and the XDoclet manual.
/** * Holds details about an account. * @hibernate.class table="ACCOUNT" * @hibernate.query name="findAccountsByBorrowerName" * query="select a from accounts a where a.name = :borrowerName" */ public class Account { ... }
Working out the annotations can be tricky sometimes; my normal technique is to either look at one I prepared earlier, or to do the configuration by hand so I know what I’m trying to achieve, then to roll it back to to the XDoclet comments.