Hibernate Hopes

I've been working with Hibernate's newCriteria interface for the past few days, and since it's not working as I hoped - I thought I'd detail my thoughts. The following code allows me to use one search parameter (wrapped with % signs) and find all matching columns with this value. This allows for a very simple UI where I have a single search box on a list screen that allows for filtering the displayed items. Here's an example:

Warning: These may not compile as thorough editing has taken place after I copied/pasted the code.

boolean msoFilter = (msoIds.contains(new Long(-1))) ? false : true;
boolean headendFilter = (headendIds.contains(new Long(-1))) ? false : true;

List crList = null;

String filter = "%" + filterBy + "%";

// Set query criteria - if a search term is passed in
try {
    StringBuffer query = new StringBuffer();
    query.append("from cr in class " + ChangeRequestDetail.class);

    if (headendFilter) {
        query.append(" where cr.headendId in (:headends)");
    } else if (msoFilter) {
        query.append(" where cr.msoId in (:msos)");
    }

    if (!StringUtils.isEmpty(filterBy)) {
        if (log.isDebugEnabled()) {
            log.debug("filtering on search term: " +
                      filterBy.toLowerCase());
        }

        if (headendFilter || msoFilter) {
            query.append(" and ");
        } else {
            query.append(" where ");
        }

        query.append(" ((cr.crId like :searchTerm)");
        query.append(" or (lower(cr.stateDesc) like :searchTerm)");
        query.append(" or (lower(cr.headendName) like :searchTerm)");
        query.append(" or (cr.headendId like :searchTerm)");
        query.append(" or (lower(cr.desiredCompleteDate) like :searchTerm)");
        query.append(" or (lower(cr.effectiveDate) like :searchTerm)");
        query.append(" or (lower(cr.requestorName) like :searchTerm))");
    }

    query.append(" order by cr.desiredCompleteDate desc");

    Query q = ses.createQuery(query.toString());

    // Bind on headends OR msos
    if (headendFilter) {
        q.setParameterList("headends", headendIds);
    } else if (msoFilter) {
        q.setParameterList("msos", msoIds);
    }

    if (!StringUtils.isEmpty(filterBy)) {
        q.setParameter("searchTerm", filter.toLowerCase());
    }

    crList = q.list();
    ...

With the new Query by Criteria feature, this should be much easier, and I'd love if it was as simple as the following:

boolean msoFilter = (msoIds.contains(new Long(-1))) ? false : true;
boolean headendFilter = (headendIds.contains(new Long(-1))) ? false : true;

List crList = null;

String filter = "%" + filterBy + "%";

// Set query criteria - if a search term is passed in
try {
    Criteria criteria = ses.createCriteria(ChangeRequestDetail.class);
    if (headendFilter) {
        criteria.add(Expression.in("headendId", headendIds));
    } else if (msoFilter) {
        criteria.add(Expression.in("msoId", msoIds));
    }
    
    if (!StringUtils.isEmpty(filterBy)) {
        if (log.isDebugEnabled()) {
           log.debug("filtering on search term: " + filterBy.toLowerCase());
        }
       
        String[] properties = {"crId", "stateDesc", "headendName", "headendId", 
                               "desiredCompleteDate", "effectiveDate", 
                               "requestorName"};
                               
        criteria.add(Expression.anyILike(properties, filter));
    }
    
    criteria.addOrder(Order.desc("desiredCompleteDate"));
    crList = criteria.list();
    ...

To make this possible, I propose the following methods be added to the Expression class in Hibernate. NOTE: I've added these methods and things don't work as I'd hoped. You can follow my progress on getting this implemented (or not) on Hibernate's SourceForge Forum.

/**
 * Apply an "ilike" constraint to any property in the
 * key set of a Map
 * 
 * @param propertyNameValues a map from property names to values
 * @return Expression
 */
public static Expression anyILike(Map propertyNameValues) {
    Disjunction disj = disjunction();
    Iterator iter = propertyNameValues.entrySet().iterator();
    while ( iter.hasNext() ) {
        Map.Entry me = (Map.Entry) iter.next();
        disj.add( ilike( (String) me.getKey(), me.getValue() ) );
    }
    return disj;
}

/**
 * Apply an "ilike" constraint to all properties based on a single value.
 * 
 * @param propertyNames
 * @param value
 * @return Expression
 */
public static Expression anyILike(String[] propertyNames, String value) {
    Disjunction disj = disjunction();
    for (int i=0; i < propertyNames.length; i++) {
        disj.add( ilike ( propertyNames[i], value));
    }
    return disj;
}

/**
 * Apply a "like" constraint to any property in the
 * key set of a Map
 * 
 * @param propertyNameValues a map from property names to values
 * @return Expression
 */
public static Expression anyLike(Map propertyNameValues) {
    Disjunction disj = disjunction();
    Iterator iter = propertyNameValues.entrySet().iterator();
    while ( iter.hasNext() ) {
        Map.Entry me = (Map.Entry) iter.next();
        disj.add( like( (String) me.getKey(), me.getValue() );
    }
    return disj;
}

Leave a comment