Ok, boys and girls. I know it's a necro and Hibernate Criteria Api was deprecated long ago. But still there are systems which use this API, so hope it will be useful.
I could not find a way to do it with built-in hibernate projections, so I've decided to make my own ones. First of all we will need to create a new projection class which will produce nothing in SELECT clause, but still have it in group clause.
public class NoPropertyGroupProjection extends SimpleProjection {
private String propertyName;
protected NoPropertyGroupProjection(String propertyName) {
this.propertyName = propertyName;
}
@Override
public boolean isGrouped() {
return true;
}
@Override
public Type[] getTypes(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
return new Type[] { };
}
@Override
public String toSqlString(Criteria criteria, int position, CriteriaQuery criteriaQuery) throws HibernateException {
return "";
}
@Override
public String toGroupSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
return StringHelper.join( ", ", criteriaQuery.getColumns( propertyName, criteria ) );
}
@Override
public String toString() {
return propertyName;
}
}
That's a copy of PropertyProjection from the version of Hibernate I have with some changes.
It won't work alone (it is just much complicated to force it work alone), but in most cases we still need something to be selected.
So the next thing we need is to fix ProjectionList as it will break with empty column we're trying to pass it. So, here's the next class. Shame elements list is private, but we have sufficient getters to achieve our goal.
public class ProjectionListWithOnlyGroupBySupport extends ProjectionList {
@Override
public String toSqlString(Criteria criteria, int loc, CriteriaQuery criteriaQuery) throws HibernateException {
final StringBuilder buf = new StringBuilder();
String separator = "";
for ( int i = 0; i < this.getLength(); i++ ) {
Projection projection = this.getProjection(i);
String addition = projection.toSqlString( criteria, loc, criteriaQuery );
if (!"".equals(addition)) {
buf.append(separator).append(addition);
loc += getColumnAliases(loc, criteria, criteriaQuery, projection).length;
separator = ", ";
}
}
return buf.toString();
}
private static String[] getColumnAliases(int loc, Criteria criteria, CriteriaQuery criteriaQuery, Projection projection) {
return projection instanceof EnhancedProjection
? ( (EnhancedProjection) projection ).getColumnAliases( loc, criteria, criteriaQuery )
: projection.getColumnAliases( loc );
}
}
Again, small adjustments for the original class. Now we have everything needed to accomplish our goal. But for convenience we will create one more class.
public final class AdvancedProjections {
public static NoPropertyGroupProjection groupBy(String propertyName) {
return new NoPropertyGroupProjection( propertyName );
}
public static ProjectionList projectionList() {
return new ProjectionListWithOnlyGroupBySupport();
}
}
After we've created all these classes, we can change the code from the question:
final Criteria agcriteria = createCriteria(someclass.class);
agcriteria.add(Restrictions.in("eventId", listOfEventIds));
agcriteria.add(Restrictions.ne("action", "T"));
agcriteria.add(
Restrictions.between("modifyDate", lastProcessedTime,
batchStartTime));
agcriteria.setProjection(AdvancedProjections.projectionList()
.add(Projections.max("assetHistoryId"))
.add(AdvancedProjections.groupBy("assetIdentifier"))
.add(AdvancedProjections.groupBy("eventId")));
val = agcriteria.list();
Voila!