JPA - Criteria API and EmbeddedId
Asked Answered
D

3

37

I want to use criteria to make the following query. I have an Entity with EmbeddedId defined:

 @Entity
 @Table(name="TB_INTERFASES")
 public class Interfase implements Serializable {

  @EmbeddedId
  private InterfaseId id;
 }

 @Embeddable
 public class InterfaseId implements Serializable {
  @Column(name="CLASE")
  private String clase;
 }

And the criteria query that i am trying to do is:

 CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
 CriteriaQuery<Interfase> criteriaQuery = criteriaBuilder.createQuery(Interfase.class);
 Root<Interfase> entity = criteriaQuery.from(Interfase.class);
 criteriaQuery.where(
   criteriaBuilder.equal(entity.get("clase"), "Clase"),
 );

But this is throwing an IllegalArgumentException:

java.lang.IllegalArgumentException: Not an managed type: class InterfaseId

i've tried with this queries too:

 Root<Interfase> entity = criteriaQuery.from(Interfase.class);
 criteriaQuery.where(
   criteriaBuilder.equal(entity.get("id").get("clase"), "Clase"),
 );

and this one too...

 Root<Interfase> entity = criteriaQuery.from(Interfase.class);
 criteriaQuery.where(
   criteriaBuilder.equal(entity.get("id.clase", "Clase"),
 );

with no luck. So my question is how can i make a query with criteria when my classes are using Embedded and EmbeddedId annotations?

Thanks!. Mauro.

Dangle answered 3/11, 2010 at 5:25 Comment(0)
M
67

You need to use path navigation to access the attribute(s) of the Embeddable. Here is an example from the JPA 2.0 specification (using the static metamodel):

6.5.5 Path Navigation

...

In the following example, ContactInfo is an embeddable class consisting of an address and set of phones. Phone is an entity.

CriteriaQuery<Vendor> q = cb.createQuery(Vendor.class);
Root<Employee> emp = q.from(Employee.class);
Join<ContactInfo, Phone> phone =
    emp.join(Employee_.contactInfo).join(ContactInfo_.phones);
q.where(cb.equal(emp.get(Employee_.contactInfo)
                    .get(ContactInfo_.address)
                    .get(Address_.zipcode), "95054"))
    .select(phone.get(Phone_.vendor));

The following Java Persistence query language query is equivalent:

SELECT p.vendor
FROM Employee e JOIN e.contactInfo.phones p
WHERE e.contactInfo.address.zipcode = '95054'

So in your case, I think you'll need something like this:

criteriaBuilder.equal(entity.get("id").get("clase"), "Referencia 111")

References

  • JPA 2.0 Specification
    • Section 6.5.5 "Path Navigation"

Update: I've tested the provided entities with Hibernate EntityManager 3.5.6 and the following query:

CriteriaBuilder builder = em.getCriteriaBuilder();

CriteriaQuery<Interfase> criteria = builder.createQuery(Interfase.class);
Root<Interfase> interfaseRoot = criteria.from(Interfase.class);
criteria.select(interfaseRoot);
criteria.where(builder.equal(interfaseRoot.get("id").get("clase"), 
    "Referencia 111"));

List<Interfase> interfases = em.createQuery(criteria).getResultList();

runs fine and generates the following SQL:

17:20:26.893 [main] DEBUG org.hibernate.SQL - 
    select
        interfase0_.CLASE as CLASE31_ 
    from
        TB_INTERFASES interfase0_ 
    where
        interfase0_.CLASE=?
17:20:26.895 [main] TRACE org.hibernate.type.StringType - binding 'Referencia 111' to parameter: 1

Works as expected.

Manlove answered 3/11, 2010 at 6:19 Comment(8)
Hi Pascal, I tried that without luck. As i said in the post, when i do entity.get("id"), i get an IllegalArgumentException. Any Suggestion?Dangle
@Mauro No you didn't say that in your question. What JPA implementation are you using?Manlove
@Pascal, thanks for your response again... i'm using Hibernate version 3.5.0-Beta2. The exception say that the InterfaseId it's a not managed entity. Specifically IllegalArgumentException: Not an managed type: class InterfaseIdDangle
@Mauro My point is that nowhere in your question you wrote that you tried entity.get("id"), readers have to guess what you tried, which is not good. Anyway, I'll give your mapping a try with another implementation.Manlove
@Pascal sorry for the misunderstanding. I've edited the question, so i hope this time be more clear on what I've tried to do. I will try with the recent version of Hibernate (3.6) or Eclipse Link and i will back with the results, btw... i think that is a basic query (Navigate over compound property - in this case Embeeded Class) so it's rare that hibernate don't support this type of queries. Thanks!.Dangle
@Mauro No problem. It's just that things that might be obvious for you aren't necessarily obvious for readers and avoiding any possible ambiguity is a good thing when writing a question :) I've updated my answer, the query works with Hibernate (at least with the version I used).Manlove
@Pascal it's working right now. I've updated the hibernate entity manager to version 3.6.0.Final and run the same code and just work. Thanks 4 all!. Mauro.Dangle
@Pascal, thank you very much for your answers, they are always very helpful!Bareheaded
T
0

It is an old question, but anyway...

Another extremely simple solution is

InterfaseId id = new InterfaseId();
id.setClase("Clase");
Interfase found = em.find(id);

Except if you trying to do something else besides what you asked, this is the "sane" way to do it. A query on the id will return maximum one result.

Taenia answered 17/10, 2016 at 21:27 Comment(0)
S
0

Try to copy and paste the metamodel classes into the same folder where your entities are saved (in NetBeans 8.2 they are automatically generated and have the same name of your entity but with an underscore at the end. Should be something like Interfase_ and InterfaseId_).

Force the import of the metamodel classes Interfase_ and InterfaseId_ and refer to the desired field.

CriteriaBuilder builder = em.getCriteriaBuilder();

CriteriaQuery<Interfase> criteria = builder.createQuery(Interfase.class);
Root<Interfase> interfaseRoot = criteria.from(Interfase.class);
criteria.select(interfaseRoot);
criteria.where(builder.equal(interfaseRoot.get(Interfase_.id).get(InterfaseId_.clase),"Referencia 111"));
List<Interfase> interfases = em.createQuery(criteria).getResultList();
Squawk answered 7/12, 2017 at 18:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.