Using different projection functions in Hibernate criteria API based in Dialect
Question
I want to use a standard deviation projection in a query that Im constructing using the criteria API. I can do something simply like this
public class StdDevProjection extends AggregateProjection {
public StdDevProjection(String propertyName) {
super("stddev", propertyName);
}
public Type[] getTypes(Criteria criteria, CriteriaQuery criteriaQuery)
throws HibernateException {
return new Type[] { Hibernate.DOUBLE };
}
}
and then I can use it with my criteria as:
myCriteriea.setProjection(new StdDevProjection(myproperty));
Thats all good. But my problem is that I use HSQLDB for any db unit tests etc, whereas we use Oracle for deployment. The stddev function works perfectly in oracle, but its not there in HSQLDB. HSQLDB has stddev_pop and stddev_samp . So is there someway I can use a different function based on the dialect.
I maybe can extend the HSQL dialect to register the "stddev" to the appropriate HSQL function, but then im not sure how to use an hsql function in a query constructed using the Criteria API.
Any help would be gret.
Thanks
Solution
Using the dialect is the right approach (although I've got to say that using different database engines for testing vs deployment seems a bit iffy). You can do the following:
- Extend HSQL dialect and use
registerFunction()
to register an appropriatestddev
implementation. - Override
toSqlString()
method in yourStdDevProjection
class and have Dialect render function name.
Something like:
public String toSqlString(Criteria criteria, int loc, CriteriaQuery criteriaQuery) throws HibernateException {
Dialect dialect = criteriaQuery.getFactory().getDialect();
SQLFunction function = (SQLFunction) dialect.getFunctions().get(this.aggregate);
//TODO: throw an exception if function is not registered
//create function argument array
List functionArgs = new ArrayList(1);
functionArgs.add(criteriaQuery.getColumn(criteria, propertyName));
return new StringBuffer()
.append(function.render(functionArgs, criteriaQuery.getFactory()))
.append(" as y").append(loc).append('_')
.toString();
}
OTHER TIPS
public class StdDevProjection extends AggregateProjection {
/**
*
*/
private static final long serialVersionUID = -7056189336427534748L;
private String aggregateName = null;
public StdDevProjection(String propertyName) {
super("stddev", propertyName);
this.aggregateName = "stddev";
}
@Override
public Type[] getTypes(Criteria criteria, CriteriaQuery criteriaQuery)
throws HibernateException {
return new Type[] { Hibernate.DOUBLE };
}
@Override
public String toSqlString(Criteria criteria, int loc, CriteriaQuery criteriaQuery)
throws HibernateException {
Dialect dialect = criteriaQuery.getFactory().getDialect();
SQLFunction function = (SQLFunction)dialect.getFunctions().get(this.aggregateName);
if(function == null) {
throw new HibernateException("Couldnt find function for aggregate: " + aggregateName + " in Dialect: " + dialect);
}
//create function argument array
List functionArgs = new ArrayList(1);
functionArgs.add(criteriaQuery.getColumn(criteria, propertyName));
return new StringBuffer()
.append(function.render(functionArgs, criteriaQuery.getFactory()))
.append(" as y").append(loc).append('_')
.toString();
}
}
and this is what the dialect looks like
public class ExtendedHSQLDialect extends HSQLDialect {
public ExtendedHSQLDialect() {
super();
registerFunction("stddev", new StandardSQLFunction("stddev_pop",Hibernate.DOUBLE));
}
}
Thanks ChssPly76 :)