1

I have an entity structure like this:

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="CLASS_TYPE", discriminatorType= DiscriminatorType.STRING)
@DiscriminatorValue(value="SUPER")
public class SuperEntity implements Serializable {
    private String anyBaseMember;
... blah blah
}

@Entity    
@DiscriminatorValue(value="DERIVED")
public class DerivedEntity extends SuperEntity {
    private String restrictingMember;
    ... getter / setter / blah blah
}

Now the intermediate goal is to select all records of the table regardless of the class type, i.e. I have to select all SuperEntity classes, but with a where clause:

CriteriaQuery<SuperEntity> query = criteriaBuilder.createQuery(SuperEntity.class);
Root root = query.from(SuperEntity.class);        
query.where(criteriaBuilder.equal(root.get(SuperEntity_.anyBaseMember), "BLAHBLAHBLAH"));

Now, this works very fine an delivers all kinds of entities, SuperEntity and DerivedEntity with the restriction of this certain base member. Now this is where the fun starts: I have to do a further restriction on the restrictingMember of the derived class, if and only if the current record is of that type of the derived class. This is what I tried:

Root root = query.from(SuperEntity.class);

query.where(criteriaBuilder.and(
    criteriaBuilder.equal(root.get(SuperEntity_.anyBaseMember), "BLAHBLAHBLAH"),
    criteriaBuilder.or(
        criteriaBuilder.notEqual(root.type(), DerivedClass.class),
        criteriaBuilder.equal(((Path) root.as(DerivedClass.class)).get(DerivedClass_.restrictingMember), "SNAFU")
        )
));      

To make long things short: It doesn't work:

Exception Description: Invalid query key [restrictingMember] in expression.

It doesn't work, because it seems impossible to cast the root to any other class. At least it doesn't work with .as()

My current approach to get my job done, is to limit only on all members of the SuperClass and then filtering out all other restrictions in a for-loop. Not the state-of-the-art, but my only idea so far.

Edit: Found the solution!

Root rootBase = query.from(SuperEntity.class);
Root rootDerived = query.from(DerivedClass.class);

query.where(criteriaBuilder.and(
    criteriaBuilder.equal(rootBase, rootDerived),
    criteriaBuilder.equal(rootBase.get(SuperEntity_.anyBaseMember), "BLAHBLAHBLAH"),
    criteriaBuilder.or(
        criteriaBuilder.notEqual(rootBase.type(), DerivedClass.class),
        criteriaBuilder.equal(rootDerived.get(DerivedClass_.restrictingMember), "SNAFU")
        )
));      

Thanks a lot for your ideas. Ralf

Hennes
  • 1,340
  • 1
  • 10
  • 26
  • Have you tried obtaining the expression you need using a separate "derivedRoot"? Something like `cb.equal(query.from(DerivedClass.class).get(DerivedClass_.restrictingMember), "SNAFU")`? It's just a shot in the dark here, but the first thought that comes to mind, since `Root#as()` specifically says it won't change the runtime type and warns about throwing a runtime exception if you use it for what's not intended for. – rdcrng Aug 14 '13 at 11:56
  • Hi, using a non-connected "from" would result in a cartesian merge, i.e. you multiply the number of rows of the table by itself. – Hennes Aug 14 '13 at 14:05

1 Answers1

0

I know you can query for the discriminator value on Named Queries by using the "class" property, like in select e from SuperEntity e where e.class ='DERIVED'. I know it is not using criteria, but perhaps it can be of help.

Jido
  • 1
  • This is what the .type() is for and that works fine. The problem is accessing the member field of the derived class. – Hennes Aug 15 '13 at 07:35