web-dev-qa-db-fra.com

API de critères JPA - Comment ajouter une clause JOIN (aussi générale que possible)

J'essaie de construire des requêtes de manière dynamique, et ma prochaine cible est d'ajouter des clauses JOIN (je ne sais pas comment utiliser l'API).

À ce jour, par exemple, ce code fonctionne pour moi:

...
Class baseClass;   
...
CriteriaBuilder cb = JpaHandle.get().getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(this.baseClass);
Root entity_ = cq.from(this.baseClass); 
Predicate restrictions = null;
...
restrictions = cb.conjunction();
restrictions = cb.and(restrictions, entity_.get("id").in(this.listId));
...
cq.where(restrictions);
...
Query qry = JpaHandle.get().createQuery(cq);

(Remarque: JpaHandle provient de la mise en œuvre de Wicket-JPA)

Mon désir est d'ajouter une clause JOIN (aussi générique que possible)!

J'ai les annotations particulières dans les classes (this.baseClass)

Par exemple :

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "assay_id", nullable = false)

Alors, existe-t-il un moyen de quelque chose comme ça dans la JPA standard? (Remarque: cela ne compile pas)

Voici quelques approches d'échec pratiques:

...
Join<Experiment,Assay> experimentAssays = entity_.join( entity_.get("assay_id") );

Ou comme ça:

...
CriteriaQuery<Customer> q = cb.createQuery(Customer.class);
Root<Customer> c = q.from(Customer.class);
SetJoin<Customer, PurchaseOrder> o = c.join(Customer_.orders);

Pour moi, si ça pouvait être plus générique que possible ce sera génial ...:

...
Join joinClause = entity_join(entity_.get("assay_id"), entity2_.get("id"));

Bien sûr, j'ai les annotations particulières dans les classes (this.baseClass)

Merci pour votre temps. J'apprécierai toutes sortes de commentaires!

30
Mandor

Peut-être que l'extrait suivant du Chapitre 23 - Utilisation de l'API Criteria pour créer des requêtes du tutoriel Java EE 6 jettera un peu de lumière (en fait, je suggère de lire le tout Chapitre 23):

Recherche de relations à l'aide de jointures

Pour les requêtes qui accèdent aux classes d'entités liées, la requête doit définir une jointure à l'entité associée en appelant l'un des From.join méthodes sur l'objet racine de la requête ou un autre objet join. Les méthodes de jointure sont similaires au mot clé JOIN dans JPQL.

La cible de la jointure utilise la classe Metamodel de type EntityType<T> pour spécifier le champ persistant ou la propriété de l'entité jointe.

Les méthodes de jointure renvoient un objet de type Join<X, Y>, où X est l'entité source et Y est la cible de la jointure.

Exemple 23-10 Joindre une requête

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Metamodel m = em.getMetamodel();
EntityType<Pet> Pet_ = m.entity(Pet.class);

Root<Pet> pet = cq.from(Pet.class);
Join<Pet, Owner> owner = pet.join(Pet_.owners);

Les jointures peuvent être chaînées ensemble pour naviguer vers les entités liées de l'entité cible sans avoir à créer un Join<X, Y> instance pour chaque jointure.

Exemple 23-11 Le chaînage se joint à une requête

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Metamodel m = em.getMetamodel();
EntityType<Pet> Pet_ = m.entity(Pet.class);
EntityType<Owner> Owner_ = m.entity(Owner.class);

Root<Pet> pet = cq.from(Pet.class);
Join<Owner, Address> address = cq.join(Pet_.owners).join(Owner_.addresses);

Cela étant dit, j'ai quelques remarques supplémentaires:

Tout d'abord, la ligne suivante dans votre code:

Root entity_ = cq.from(this.baseClass);

Cela me fait penser que vous avez en quelque sorte manqué la partie Classes de métamodèles statiques. Classes de métamodèle telles que Pet_ dans l'exemple cité sont utilisés pour décrire les méta-informations d'une classe persistante. Ils sont généralement générés à l'aide d'un processeur d'annotation ( classes de métamodèles canoniques) ou peuvent être écrits par le développeur (métamodèle non canonique). Mais votre syntaxe a l'air bizarre, je pense que vous essayez d'imiter quelque chose que vous avez manqué.

Deuxièmement, je pense vraiment que vous devriez oublier cela assay_id clé étrangère, vous êtes sur le mauvais chemin ici. Vous devez vraiment commencer à penser à l'objet et à l'association, pas aux tables et aux colonnes.

Troisièmement, je ne suis pas vraiment sûr de comprendre ce que vous voulez dire exactement en ajoutant une clause JOIN aussi générique que possible et à quoi ressemble votre modèle d'objet, puisque vous ne l'avez pas fourni (voir précédent point). Il est donc tout simplement impossible de répondre plus précisément à votre question.

Pour résumer, je pense que vous avez besoin de lire un peu plus sur les critères JPA 2.0 et l'API Metamodel et je recommande vivement les ressources ci-dessous comme point de départ.

Voir également

Question connexe

40
Pascal Thivent

En fait, vous n'avez pas à gérer le métamodèle statique si vos annotations étaient correctes.

Avec les entités suivantes:

@Entity
public class Pet {
  @Id
  protected Long id;
  protected String name;
  protected String color;
  @ManyToOne
  protected Set<Owner> owners;
}

@Entity
public class Owner {
  @Id
  protected Long id;
  protected String name;
}

Vous pouvez utiliser ceci:

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Metamodel m = em.getMetamodel();
EntityType<Pet> petMetaModel = m.entity(Pet.class);

Root<Pet> pet = cq.from(Pet.class);
Join<Pet, Owner> owner = pet.join(petMetaModel.getSet("owners", Owner.class));
14
Christophe Opoix

Vous n'avez pas besoin d'apprendre JPA. Vous pouvez utiliser mes critères faciles pour JPA2 ( https://sourceforge.net/projects/easy-criteria/files/ ). Voici l'exemple

CriteriaComposer<Pet> petCriteria CriteriaComposer.from(Pet.class).
where(Pet_.type, EQUAL, "Cat").join(Pet_.owner).where(Ower_.name,EQUAL, "foo");

List<Pet> result = CriteriaProcessor.findAllEntiry(petCriteria);

OR

List<Tuple> result =  CriteriaProcessor.findAllTuple(petCriteria);
5
user793442

Attention! Il y a un certain nombre d'erreurs sur l'exemple de Sun JPA 2 et le contenu collé résultant dans la réponse de Pascal. Veuillez consulter cet article .

Ce message et l'exemple Sun Java EE 6 JPA 2 ont vraiment retenu ma compréhension de JPA 2. Après avoir parcouru les manuels Hibernate et OpenJPA et pensé que j'avais une bonne compréhension de JPA 2, je encore confus après en revenant à ce poste.

5
Chris Harris