Mark February 2016

Can Hibernate Criteria query for subclasses using Restrictions.in?

I have a number of classes mapped as an inheritance hierarchy into a single table.

I would like to use the following Criteria query but this results in a ClassCastException since Class cannot be cast to String.

Set<Class> childClasses = new HashSet<>();
childClasses.add(Child1.class);
childClasses.add(Child2.class);

session.createCriteria(Parent.class)
       .add(Restrictions.in("class", childClasses)
       .list();

I note that Hibernate does support specifying a single class using Restrictions.eq("class", childClass) so I can workaround by using a Disjunction'. I also know that this would work if my resriction was based on the discriminator strings for each subclass but I would prefer not to use these.

It is possible to use Criteria in this manner? The accepted answer to this question suggests that it works when the class is a property of the class you are basing your Criteria on but it doesn't seem to work in the case I've shown above.

Answers


Dragan Bozanovic February 2016

Looking at the source code, I would say that this is a Hibernate bug.

For example, SimpleExpression (returned by Restrictions.eq) calls criteriaQuery.getTypedValue which handles conversion of Class value to the corresponding discriminator value.

InExpression (returned by Restrictions.in) adds values as they are, without conversion. That's why you get ClassCastException, because later an attempt to cast Class to String is made (obviously the type of your discriminator value is String).

You could avoid using this form until it's fixed (you already suggested the proper workarounds), or, if you would really like to stick to using Class objects directly, then you could implement custom InExpression in your project. For example, something like:

public class ClassInExpression extends InExpression {
    private static final String CLASS = "class";
    private final Collection<Class> values;

    public ClassInExpression(Collection<Class> values) {
        super(CLASS, values.toArray(new Object[values.size()]));
        this.values = values;
    }

    @Override
    public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery) {
        if (criteriaQuery.getTypeUsingProjection(criteria, CLASS).isComponentType()) {
            return super.getTypedValues(criteria, c 

Post Status

Asked in February 2016
Viewed 1,957 times
Voted 5
Answered 1 times

Search




Leave an answer