Search This Blog

Thursday 17 January 2013

Creating a new Criterion

We have seen Hibernate's set of Restrictions and they cover a very large range of queries. However Hibernate also allows the creation of a custom Criterion that can be used in the criteria.
I created a simple Criterion class that is used to check the length of string properties.
@SuppressWarnings("serial")
public class LengthExpression implements Criterion {
    private final String propertyName;
    private final int value;

    public LengthExpression(final String propertyName, final int value) {
        this.propertyName = propertyName;
        this.value = value;
    }

    @Override
    public String toSqlString(final Criteria criteria,
            final CriteriaQuery criteriaQuery) throws HibernateException {
        criteriaQuery.getFactory().getDialect();
        final String[] columns = criteriaQuery.getColumnsUsingProjection(
                criteria, this.propertyName);
        final String queryFragment = "length(" + columns[0] + ") = ?";
        return queryFragment;
    }

    @Override
    public TypedValue[] getTypedValues(final Criteria criteria,
            final CriteriaQuery criteriaQuery) throws HibernateException {
        // An ordered pair of a value and its Hibernate type
        return new TypedValue[] { new TypedValue(Hibernate.INTEGER,
                Integer.valueOf(value), EntityMode.POJO) };
    }
}
In the above code:
  • The class implements the Criterion Interface which exposes two methods.
  • The  toSqlString() method is what generates the actual sql fragment. As we work with prepared statements, "?" ares used. In this case the sql length function is used. The alias used for property in the actual query is obtained using the getColumnsUsingProjection() method.
  • The getTypedValues() method tells Hibernate about the data Type to be used in the query. In this case the parameter will be set using the preparedStatement.setInt() method.
To test the code, I used it in a criteria example:
public static void testCustomCriterion() {
    final Session session = sessionFactory.openSession();
    Criteria criteria = session.createCriteria(Entity.class);
    criteria.add(new LengthExpression("name", 7));        
    List<Entity> entities = criteria.list(); 
    System.out.println(entities);
}
The result of the execution is below:
3484 [main] DEBUG org.hibernate.SQL  - 
    /* criteria query */ 
    select
        this_.ID as ID0_0_,
        this_.NAME as NAME0_0_,
        this_.DATE as DATE0_0_,
        this_.MASTER_ID as MASTER4_0_0_ 
    from
        ENTITY this_ 
    where
        length(this_.NAME) = ?
...
3484 [main] DEBUG org.hibernate.jdbc.AbstractBatcher  - preparing statement
3516 [main] DEBUG org.hibernate.type.IntegerType  - binding '7' to parameter: 1
...
3625 [main] DEBUG org.hibernate.connection.DriverManagerConnectionProvider  - re
turning connection to pool, pool size: 1
[[Entity] : ( id 2 , data : entity1 , master.Id : 1 , date : null )], [Entity] :
 ( id 3 , data : entity2 , master.Id : 1 , date : null )]]
As can be seen the query generated includes the length() function. Also the parameter(7) was bound to the prepared statement.

1 comment: