web-dev-qa-db-fra.com

Liste de passage JPA à la clause IN dans une requête native nommée

Je sais que je peux transmettre une liste à une requête nommée dans JPA, mais qu'en est-il de NamedNativeQuery? J'ai essayé de nombreuses façons, mais je ne peux toujours pas passer la liste à une requête NamedNativeQuery. Quelqu'un sait comment passer une liste à la clause in dans NamedNativeQuery? Merci beaucoup!

Le NamedNativeQuery est comme ci-dessous:

@NamedNativeQuery(
   name="User.findByUserIdList", 
   query="select u.user_id, u.dob, u.name, u.sex, u.address from user u "+
         "where u.user_id in (?userIdList)"
)

et ça s'appelle comme ça:

List<Object[]> userList = em.createNamedQuery("User.findByUserIdList").setParameter("userIdList", list).getResultList();

Cependant, le résultat n'est pas comme prévu.

System.out.println(userList.size());  //output 1

Object[] user = userList.get(0);
System.out.println(user.length);   //expected 5 but result is 3
System.out.println(user[0]);       //output MDAVERSION which is not a user_id
System.out.println(user[1]);       //output 5
System.out.println(user[2]);       //output 7
28
Li Ho Yin

Une liste n'est pas un paramètre valide pour une requête SQL native, car elle ne peut pas être liée dans JDBC. Vous devez avoir un paramètre pour chaque argument de la liste.

où u.user_id dans (? id1,? id2)

Ceci est pris en charge via JPQL, mais pas SQL, vous pouvez donc utiliser JPQL au lieu d'une requête native.

Certains fournisseurs JPA peuvent prendre en charge cela, vous pouvez donc vouloir enregistrer un bogue avec votre fournisseur.

16
James

La réponse acceptée ci-dessus n'est pas correcte et m'a conduit hors de piste pendant plusieurs jours !!

JPA et Hibernate acceptent tous deux les collections en requête native à l'aide de Query.

Vous avez juste besoin de faire

String nativeQuery = "Select * from A where name in :names"; //use (:names) for older versions of hibernate
Query q = em.createNativeQuery(nativeQuery);
q.setParameter("names", l);

Reportez-vous également aux réponses ici qui suggèrent la même chose (j'ai choisi l'exemple ci-dessus de l'un d'eux)

  1. référence 1
  2. Référence 2 qui mentionne les cas où la paranthèse fonctionne et qui donne la liste comme paramètre
10
HopeKing

En fonction de votre base de données/fournisseur/pilote/etc., Vous pouvez en fait passer une liste en tant que paramètre lié à une requête native JPA.

Par exemple, avec Postgres et EclipseLink, les travaux suivants (renvoyant true), montrent des tableaux multidimensionnels et comment obtenir un tableau de double précision. (Faire SELECT pg_type.* FROM pg_catalog.pg_type pour les autres types; probablement ceux avec _, mais retirez-le avant de l'utiliser.)

Array test = entityManager.unwrap(Connection.class).createArrayOf("float8", new Double[][] { { 1.0, 2.5 }, { 4.1, 5.0 } });
Object result = entityManager.createNativeQuery("SELECT ARRAY[[CAST(1.0 as double precision), 2.5],[4.1, 5.0]] = ?").setParameter(1, test).getSingleResult();

La distribution est là, donc le tableau littéral est composé de doubles plutôt que numériques.

Plus précisément à la question - je ne sais pas comment ou si vous pouvez faire des requêtes nommées; Je pense que cela dépend, peut-être. Mais je pense que suivre fonctionnerait pour les trucs Array.

Array list = entityManager.unwrap(Connection.class).createArrayOf("int8", arrayOfUserIds);
List<Object[]> userList = entityManager.createNativeQuery("select u.* from user u "+
     "where u.user_id = ANY(?)")
     .setParameter(1, list)
     .getResultList();

Je n'ai pas le même schéma que OP, donc je n'ai pas vérifié cela exactement, mais je pense que cela devrait fonctionner - encore une fois, au moins sur Postgres EclipseLink.

En outre, la clé a été trouvée dans: http://tonaconsulting.com/postgres-and-multi-dimensions-arrays-in-jdbc/

3
Erhannis

En utilisant les données hibernate, JPA 2.1 et deltaspike, je pourrais passer une liste comme paramètre dans la requête qui contient la clause IN. ma requête est ci-dessous.

@Query(value = "SELECT DISTINCT r.* FROM EVENT AS r JOIN EVENT AS t on r.COR_UUID = t.COR_UUID where " +
        "r.eventType='Creation' and t.eventType = 'Reception' and r.EVENT_UUID in ?1", isNative = true)
public List<EventT> findDeliveredCreatedEvents(List<String> eventIds);
1
IsidIoan

Testé en JPA2 avec Hibernate en tant que fournisseur et il semble que hibernate supporte la prise d'une liste pour "IN" et cela fonctionne. (Au moins pour les requêtes nommées et je pense que ce sera similaire avec les requêtes nommées NATIVE) Ce que la mise en veille prolongée fait en interne est de générer des paramètres dynamiques, à l'intérieur de l'IN comme le nombre d'éléments dans la liste transmise.

Donc, dans votre exemple ci-dessus

List<Object[]> userList = em.createNamedQuery("User.findByUserIdList").setParameter("userIdList", list).getResultList();

Si la liste contient 2 éléments, la requête ressemblera

select u.user_id, u.dob, u.name, u.sex, u.address from user u "+
         "where u.user_id in (?, ?)

et s'il a 3 éléments on dirait

select u.user_id, u.dob, u.name, u.sex, u.address from user u "+
         "where u.user_id in (?, ?, ?)
1
Ravi Sanwal

actuellement j'utilise JPA 2.1 avec Hibernate

J'utilise également la condition IN avec une requête native. Exemple de ma requête

SELECT ... WHERE table_name.id IN (?1)

J'ai remarqué qu'il est impossible de passer une chaîne comme "id_1, id_2, id_3" en raison des limitations décrites par James

Mais lorsque vous utilisez jpa 2.1 + hibernate, il est possible de passer List of string values. Pour mon cas, le code suivant est valide:

    List<String> idList = new ArrayList<>();
    idList.add("344710");
    idList.add("574477");
    idList.add("508290");

    query.setParameter(1, idList);
0
musicccMan

Dans mon cas (EclipseLink, PostGreSQL) cela fonctionne:

    ServerSession serverSession = this.entityManager.unwrap(ServerSession.class);
    Accessor accessor = serverSession.getAccessor();
    accessor.reestablishConnection(serverSession);
    BigDecimal result;
    try {
        Array jiraIssues = accessor.getConnection().createArrayOf("numeric", mandayWorkLogQueryModel.getJiraIssues().toArray());
        Query nativeQuery = this.entityManager.createNativeQuery(projectMandayWorkLogQueryProvider.provide(mandayWorkLogQueryModel));
        nativeQuery.setParameter(1,mandayWorkLogQueryModel.getPsymbol());
        nativeQuery.setParameter(2,jiraIssues);
        nativeQuery.setParameter(3,mandayWorkLogQueryModel.getFrom());
        nativeQuery.setParameter(4,mandayWorkLogQueryModel.getTo());
        result = (BigDecimal) nativeQuery.getSingleResult();
    } catch (Exception e) {
        throw new DataAccessException(e);
    }

    return result;

Également en requête ne peut pas utiliser IN (?) Car vous obtiendrez une erreur comme:

Provoqué par: org.postgresql.util.PSQLException: ERREUR: l'opérateur n'existe pas: numérique = numérique []

'IN (?)' Doit être remplacé par '= ANY (?)'

Ma solution était basée sur le concept Erhannis.

0
Marcin Dąbrowski