Search code examples
oracle-databasehibernatesortingcriteriaxmltype

Hibernate Computed Criteria Order


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 =\


Solution

  • 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')).