15

I have the following EJB structure. Don't wonder about Animal and Inventory, these classes are only here to demonstrate the structure in a simplified way (Update: I have revised the class names to construct a better understandable example. Another implementation of IdTag might be a BarcodeId). Note that there is no inverse relationship from IdTag to Animal or Inventory, and let's assume the RfidTag.code is unique. I read Retrieving Polymorphic Hibernate Objects Using a Criteria Query and Hibernate polymorphic query but these discussions does not seem to answer my question.

public interface ItemWithIdTag
{
    IdTag getIdTag();
    void setIdTag(IdTag idTag);
}

@Entity public class Animal implements ItemWithIdTag,Serializable
{
    @Id @GeneratedValue(strategy=GenerationType.AUTO) private long id;

    @OneToOne(cascade = CascadeType.ALL)
    private IdTag idTag;
}

@Entity public class Inventory implements ItemWithIdTag,Serializable
{
    @Id @GeneratedValue(strategy=GenerationType.AUTO) private long id;

    @OneToOne(cascade = CascadeType.ALL)
    private IdTag idTag;
}

@Entity @Table(name = "IdTag") @Inheritance(strategy= InheritanceType.JOINED)
public class IdTag implements Serializable
{
    @Id @GeneratedValue(strategy=GenerationType.AUTO) private long id;
    private Date created;
}

@Entity @Table(name = "RfidTag")
public class RfidTag extends IdTag implements Serializable
{
    private String code;
}

Now I want to query either Animal or Inventory for a given RfidTag.code like Animal ejb = bean.fEntityWithRfidTag(Animal.class,"myRfIdCode");

public <T extends ItemWithIdTag> T fOwner(Class<T> type, String catName)
{
    CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
    CriteriaQuery<T> criteriaQuery = criteriaBuilder.createQuery(type);
    Root<T> from = criteriaQuery.from(type);

    Path<Object> path = from.join("idTag").get("code");

    CriteriaQuery<T> select = criteriaQuery.select(from);
    select.where(criteriaBuilder.equal(path, catName));

    TypedQuery<T> q = em.createQuery(select); 
    T result = (T)q.getSingleResult();}
    return result;
}

Unfortuately I get the following errror:

javax.ejb.EJBException: java.lang.IllegalArgumentException:
Unable to resolve attribute [code] against path [null]

I assume that this is related to the inheritance IdTag -> RfidTag and Animal only knows about IdTag and not the RfidTag.code. Are queries like this possible?

Community
  • 1
  • 1
Thor
  • 6,607
  • 13
  • 62
  • 96
  • I am not sure whether this is the problem, as I have never used generic criteria builder, but it seems to me, that the problem is not so much in the inheritance but more in the fact that the path on which you run the .get("code") method is null. At least that is what the exception states. are you sure from.join("idTag") does not return null.. And don't you need to change it to from.join("IdTag") as the name of the table is defined with a capital "I". – peshkira Jun 27 '11 at 16:46
  • As far as I know, `from.join("idTag")`does not refer the name of the table but the name of the attribute in `ItemWithIdTag`. – Thor Jun 28 '11 at 14:50
  • yepp - that is true... sorry. however, did you check the return value of the join method; is it not null. Can you post the whole stack trace to see what the nested exception is? – peshkira Jun 28 '11 at 18:30
  • Unfortunately I have not created a branch for this approach. In ten days I have access to the computer containing this approach. – Thor Jun 29 '11 at 14:49
  • @Perception Thank you for the additional bounty on this question! – Thor Jul 14 '11 at 17:54

1 Answers1

10

If you are using EclipseLink, solution is simple. Modify the Path criteria to cast to RfIdTag:

Path<Object> path = ((Path) from.join("idTag").as(RfIdTag.class)).get("code");

If you are using Hibernate, replace your method with:

public static <T extends ItemWithIdTag> T fOwner(Class<T> type, String catName) {
    CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
    CriteriaQuery<T> criteriaQuery = criteriaBuilder.createQuery(type);
    Root<T> fromType = criteriaQuery.from(type);
    Root<RfIdTag> fromRfId = criteriaQuery.from(RfIdTag.class);

    Path<Object> pathCode = fromRfId.get("code");
    Path<Object> pathIdTagType = fromType.get("idTag");
    Path<Object> pathIdTagRfId = fromRfId.get("id");

    CriteriaQuery<T> select = criteriaQuery.select(fromType);
    select.where(
            criteriaBuilder.equal(pathCode, catName),
            criteriaBuilder.equal(pathIdTagType, pathIdTagRfId));

    TypedQuery<T> q = em.createQuery(select);
    return q.getSingleResult();
}

This makes a "join" ("a filtered cartesian product") between "T" and "RfIdTag".

Eduardo Costa
  • 1,974
  • 1
  • 16
  • 22
  • 1
    Impressive! I have verified this with the simplified example and can confirm this is the correct answer. Now it's on me to add this into the real world. Thanks a lot for answering! – Thor Jul 14 '11 at 18:03