web-dev-qa-db-fra.com

JPA - API de critères et EmbeddedId

Je veux utiliser des critères pour faire la requête suivante. J'ai un Entity with EmbeddedId défini:

 @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;
 }

Et la requête de critères que j'essaie de faire est la suivante:

 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"),
 );

Mais cela jette une exception IllegalArgumentException:

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

j'ai aussi essayé avec ces requêtes:

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

et celui-ci aussi ...

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

ma question est la suivante: comment puis-je effectuer une requête avec des critères lorsque mes classes utilisent des annotations Embedded et EmbeddedId?

Merci! Mauro.

25
Mauro Monti

Vous devez utiliser la navigation par chemin pour accéder aux attributs de la variable Embeddable. Voici un exemple tiré de la spécification JPA 2.0 (utilisant le métamodèle statique):

6.5.5 Navigation dans les chemins

...

Dans l'exemple suivant, ContactInfo est une classe incorporable Consistant en une adresse et un ensemble de Téléphones. Phone est une entité.

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));

La requête de langage Java Suivante est équivalente:

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

Donc dans votre cas, je pense que vous aurez besoin de quelque chose comme ça:

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

Références

  • Spécification JPA 2.0
    • Section 6.5.5 "Navigation dans les chemins"

Mise à jour: J'ai testé les entités fournies avec Hibernate EntityManager 3.5.6 et la requête suivante:

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();

fonctionne correctement et génère le code SQL suivant:

 17: 20: 26.893 [main] DEBUG org.hibernate.SQL - 
 Sélectionnez 
 Interfase0_.CLASE comme CLASE31_ 
 Parmi 
 TB_INTERFASES interfase0_ .____.] où 
 interfase0_.CLASE =? 
 17: 20: 26.895 [main] TRACE org.hibernate.type.StringType - liant «Référence 111» au paramètre: 1 

Fonctionne comme prévu.

57
Pascal Thivent

Essayez de copier et de coller les classes metamodel dans le même dossier que celui dans lequel vos entités sont enregistrées (dans NetBeans 8.2, elles sont générées automatiquement et portent le même nom de votre entité, mais avec un trait de soulignement à la fin. Cela devrait ressembler à Interfase_ et InterfaseId_).

Forcer l’importation des metamodel classes Interfase_ et InterfaseId_ et faire référence au champ souhaité.

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();
0
MicroCharles

C'est une vieille question, mais de toute façon ...

Une autre solution extrêmement simple est

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

Sauf si vous essayez de faire autre chose que ce que vous avez demandé, c'est la façon "saine" de le faire. Une requête sur l'id retournera au maximum un résultat. 

0
Theofanis