views:

215

answers:

2

I have an Oracle XMLType column that stores the various language specific strings. I need to construct a Hibernate criteria that orders on this column. In order to do this, I need to extract the value with an Oracle function. This criteria is generated automatically by code I have written but I cannot, for the life of me, figure out how to extract the value and order on it via the criteria API. Basically, the generated SQL should look something like:

SELECT EXTRACTVALUE(title, '//value[@lang="EN"]') AS enTitle
FROM domain_object 
ORDER BY enTitle

I fiddled with projections momentarily, but they appear to execute a second select. Which I assume would cause hibernate to select ALL values and in memory sort them based on the projection? This would be very undesirable =\

+2  A: 

Ok, I found a solution. Not sure this is the best, so I will leave it open for a little while if some one wants to provide a better answer / refine my solution.

What I did was extend org.hibernate.criterion.Order thusly:

package com.mycorp.common.hibernate;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.criterion.CriteriaQuery;
import org.hibernate.criterion.Order;
import org.hibernate.engine.SessionFactoryImplementor;

import com.mycorp.LocalizationUtil;

public class LocalStringOrder extends Order {
    private static final long serialVersionUID = 1L;

    private boolean ascending;
    private String  propName;

    public LocalStringOrder(String prop, boolean asc) {
        super(prop, asc);
        ascending    = asc;
        propName = prop;
    }

    public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
        String[] columns = criteriaQuery.getColumnsUsingProjection(criteria, propName);
        StringBuffer fragment = new StringBuffer();
        for ( int i=0; i<columns.length; i++ ) {
            SessionFactoryImplementor factory = criteriaQuery.getFactory();
            fragment.append( factory.getDialect().getLowercaseFunction() )
            .append('(');
            fragment.append("EXTRACTVALUE(");
            fragment.append( columns[i] );
            fragment.append(", '//value[@lang=\"" + 
                LocalizationUtil.getPreferedLanguage().name() + 
                "\"')");
            fragment.append(')');
            fragment.append( ascending ? " asc" : " desc" );
            if ( i<columns.length-1 ) fragment.append(", ");
        }
        return fragment.toString();
    }

    public static Order asc(String propertyName) {
        return new LocalStringOrder(propertyName, true);
    }


    public static Order desc(String propertyName) {
        return new LocalStringOrder(propertyName, false);
    }
}

Then it was just a matter of saying criteria.addOrder(LocalStringOrder.asc('prop')).

Ambience
+1This looks good to me. Wish I'd thought of it for you! The other thing I thought of was to create a view and query that instead, which might be clearer to some. But I prefer your solution.
Don Roby
A: 

Another general solution is NativeSQLOrder, see http://opensource.atlassian.com/projects/hibernate/browse/HHH-2381. I do not undestand, why this feature is not in Hibernate yet.

xmedeko