13 votes

Comment créer une requête de type 'instance de' en JPA 2.0 ?

Supposons que nous ayons une entité abstraite @Entity Animal, et plusieurs classes d'entité qui étendent Animal, dont Dog, Cat, Monkey et Bat.

Comment puis-je filtrer les résultats en fonction de la classe d'entité étendue ?

Exemple : Il y a des cases à cocher où l'utilisateur peut sélectionner les entités à récupérer.

[ ] Chien
[X] Chat
[X] Singe
[ ] Chauve-souris

Maintenant, je veux récupérer les entités avec une requête (Named)Query définie dans la classe Animal. Quels paramètres de requête puis-je mettre dans la requête pour que seuls les objets Chat et Singe soient renvoyés ?

33voto

JB Nizet Points 250258

Je ne suis pas absolument sûr que ce soit pris en charge par JPA, mais la manière de le faire dans Hibernate, indépendamment de la stratégie d'héritage, et donc même si vous n'avez pas de discriminateur (ou ne l'avez pas mappé en tant que propriété) est d'utiliser la propriété class implicite :

String jpql = "select a from Animal a where a.class in (:classes)";
Query q = em.createQuery(jpql).setParameter("classes", 
                                            Arrays.asList(Cat.class, Monkey.class));

EDIT :

Je viens de trouver qu'il est possible en JPA2 d'utiliser l'opérateur TYPE :

String jpql = "SELECT a FROM Animal a WHERE TYPE(a) IN :classes";
Query q = em.createQuery(jpql).setParameter("classes", 
                                            Arrays.asList(Cat.class, Monkey.class));

4voto

Shivan Dragon Points 8626

Vous pouvez utiliser la colonne de discriminant et sa valeur pour rechercher uniquement certains sous-types d'un type donné. Par défaut, le nom de la colonne discriminant est DTYPE en JPA, le type est String et la valeur est le nom de la classe. Cependant, vous pouvez remplacer cela en ajoutant l'annotation de niveau de classe @DiscriminatorColumn(name="KIND", discriminatorType=DiscriminatorType.INTEGER) (pour le nom et le type de la colonne discriminante) et @DiscriminatorValue("1") (pour la valeur de discriminant spécifique pour une certaine classe). Vous pouvez ensuite utiliser cela dans la clause WHERE de votre requête JPQL pour récupérer uniquement certains sous-types, comme : WHERE DTYPE="Chien" OR DTYPE="Chat"

0voto

Archie Points 2742

Voici ce que j'utilise :

    /**
     * Construit un "instanceof" {@link Predicate} pour un type d'entité.
     */
    @SuppressWarnings("unchecked")
    public static Predicate instanceOf(EntityManager entityManager, Path path, Class type) {
        Preconditions.checkArgument(entityManager != null, "null entityManager");
        Preconditions.checkArgument(path != null, "null path");
        Preconditions.checkArgument(type != null, "null type");
        final Set> subtypes = entityManager.getMetamodel().getManagedTypes().stream()
          .map(ManagedType::getJavaType)
          .filter(type::isAssignableFrom)
          .collect(Collectors.toSet());
        return JPAUtil.in(entityManager.getCriteriaBuilder(), (Expression>)path.type(), subtypes);
    }

    /**
     * Construit un prédicat IN à partir d'une collection de valeurs possibles.
     */
    public static  Predicate in(CriteriaBuilder builder, Expression expr, Iterable values) {
        Preconditions.checkArgument(builder != null, "null builder");
        Preconditions.checkArgument(expr != null, "null expr");
        Preconditions.checkArgument(values != null, "null values");
        final CriteriaBuilder.In in = builder.in(expr);
        values.forEach(in::value);
        return in;
    }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X