
ulti-criteria search functionalities, which allow combinations of many optional criteria fields, are common in modern Web-enabled business applications (see
Sidebar 1. When to Use Multi-Criteria Searches). An example of this functionality is an online accommodations database (see
Figure 1) that allows users to search accommodations by country, availability, type (hotel, bed and breakfast, etc.), and capacity. All the criteria are optional, and the user may use any combination of criteria.
 | |
| Figure 1: A Multi-Criteria Search Screen |
The traditional approach for a Hibernate developer to enable this sort of functionality generally involves building an HQL (Hibernate Query Language) query on the fly, based on the search criteria the user entered. Next, he or she uses this query string to create a Hibernate Query object and then sets the non-null parameter values. The code looks something like this:
...
if (startDate != null) {
queryBuf.append(firstClause ? " where ": " and ");
queryBuf.append("a.availabiliyDate >=:startDate");
parameters.put("startDate",startDate);
firstClause = false;
}
...
(See Listing 1 for a full example. Note that the model has been simplified for the sake of readability. Accommodation availability data would normally be stored in a separate table, of course.)
Listing 1. A Traditional Approach for Building Multi-Criteria HQL Queries
public List searchAccommodation(Date startDate,
Date endDate,
Country country,
AccommodationType type,
Integer capacity)
Map parameters = new HashMap();
boolean firstClause = true;
StringBuffer queryBuf
= new StringBuffer("from Accommodation a ");
if (startDate != null) {
queryBuf.append(firstClause ? " where " : " and ");
queryBuf.append("a.availabiliyDate >= :startDate");
parameters.put("startDate",startDate);
firstClause = false;
}
if (endDate != null) {
queryBuf.append(firstClause ? " where " : " and ");
queryBuf.append("a.availabiliyDate <= :endDate");
parameters.put("endDate",endDate);
firstClause = false;
}
if (country != null) {
queryBuf.append(firstClause ? " where " : " and ");
queryBuf.append("a.country = :country");
}
if (type != null) {
queryBuf.append(firstClause ? " where " : " and ");
queryBuf.append("a.type = :type");
}
if (maxCapacity != null) {
queryBuf.append(firstClause ? " where " : " and ");
queryBuf.append("a.capacity >= :capacity");
}
String hqlQuery = queryBuf.toString();
Query query = session.createQuery(hqlQuery);
//
// Set query parameter values
//
Iterator iter = parameters.keySet().iterator();
while (iter.hasNext()) {
String name = (String) iter.next();
Object value = map.get(name);
query.setParameter(name,value);
}
//
// Execute the query
//
return query.list();
}
This approach is cumbersome and error-prone. It is also risky in a team-development context, as inexperienced developers will often take dangerous short-cuts using this approach. During code review, I've often come across multi-criteria search functions using error-prone String concatenation, direct use of query parameters in the query string, and so on:
...
if (startDate != null) {
if (firstClause) {
query = query + " where ";
} else {
query = query + " and ";
}
query += " a.availabilityDate >= '"
+ startDate + "'";
}
// And so on...
So, is there better way? Yes, there is: the Hibernate criteria API, a powerful and elegant technique for complex, dynamic search functionalities. Using the online accommodations database cited previously, this article examines the criteria API solution and its benefits.