How to refer to a subclass specific field in a CriteriaQuery where the super class is queried?
Asked Answered
S

2

11

I'm trying to achieve something like the following, using the JPA Criteria API:

SELECT b FROM Box b JOIN SpecialItem s WHERE s.specialAttr = :specialAttr

The objects are

Box

@Entity
public class Box implements Serializable {
  ...
  @ManyToOne
  @JoinColumn( name = "item_id" )
  Item item;
  ...
}

Item

@Entity
@Inheritance( strategy = InheritanceType.JOINED )
public class Item implements Serializable {
  @Id
  private String id;
  ...
}

SpecialItem

@Entity
public class SpecialItem extends Item {
  private String specialAttr;
  ...
}

My attempt

EntityManager em = getEntityManager();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery( Box.class );
Root from = cq.from( Box.class );

// Nothing to specify SpecialItem over Item!      
Join join = from.join("item", JoinType.LEFT);

// java.lang.IllegalArgumentException: Unable to 
// resolve attribute [specialAttr] against path [null]      
Path p = join.get( "specialAttr" );

Predicate predicate = cb.equal( p, "specialValue" );
cq.where( predicate );

Not surprisingly it throws an exception because specialAttr isn't a member of class Item.

How can I return all the Boxes that contain a SpecialItem, where the SpecialItem.specialAttr has some value?

Slav answered 11/6, 2013 at 10:17 Comment(1)
My problem is how can I return Boxes that contain a SpecialItem TOO? I'd be glad to hear from you.Greening
L
19

If using JPA 2.1 you might use

"SELECT b FROM Box b WHERE TREAT(b.item as SpecialItem).specialAttr = :specialAttr"

or

CriteriaQuery<Box> q = cb.createQuery(Box.class);
Root<Box> box= q.from(Box.class);
Join<Box, Item > order = box.join("item");
q.where(cb.equal(cb.treat(order, SpecialItem.class).get("specialAttr"),
    qb.parameter(String.class, "specialAttr")));
q.select(Box);
Lycian answered 11/6, 2013 at 12:21 Comment(3)
Thanks but how would I do this using the Criteria API?Slav
That's great thanks! Out if interest, is a solution possible with JPA 2.0?Slav
Yes, but in a round about way that might cause extra joins. You would need to be explicit in your joins, and build the query the other way around. Something like "select b from Box b, SpecialtyItem si where b.item = si and si.specialAttr = :specialAttr"Lycian
A
9

I just want to extend the answer of Chris for the Criteria API with generated meta-model.

CriteriaQuery<Box> q = cb.createQuery(Box.class);
Root<Box> box= q.from(Box.class);
Join<Box, Item> order = box.join(Box_.item);
q.where(cb.equal(cb.treat(order, SpecialItem.class).get(SpecialItem_.specialAttr), "searchValue");
q.select(Box);
Approach answered 5/7, 2016 at 12:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.