web-dev-qa-db-fra.com

Quelle est la "bonne" façon de convertir Hibernate Query.list () en Lister <Type>?

Je suis un débutant avec Hibernate et j'écris une méthode simple pour renvoyer une liste d'objets correspondant à un filtre spécifique. List<Foo> Semblait être un type de retour naturel.

Quoi que je fasse, je ne peux pas sembler rendre le compilateur heureux, à moins d’utiliser un @SuppressWarnings Laid.

import Java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;

public class Foo {

    public Session acquireSession() {
        // All DB opening, connection etc. removed,
        // since the problem is in compilation, not at runtime.
        return null;
    }

    @SuppressWarnings("unchecked") /* <----- */

    public List<Foo> activeObjects() {
        Session s = acquireSession();
        Query   q = s.createQuery("from foo where active");
        return (List<Foo>) q.list();
    }
}

Je voudrais me débarrasser de ce SuppressWarnings. Mais si je le fais, je reçois l'avertissement

Warning: Unchecked cast from List to List<Foo>

(Je peux l'ignorer, mais j'aimerais ne pas l'obtenir en premier lieu), et si je supprime le générique pour le rendre conforme au type de retour .list(), je reçois l'avertissement.

Warning: List is a raw type. References to generic type List<E>
should be parameterized.

J'ai remarqué que org.hibernate.mapping fait déclarer un List; mais c'est un type complètement différent - Query renvoie un Java.util.List, en tant que type brut. Je trouve étrange qu’un récent Hibernate (4.0.x) n’implémente pas les types paramétrés. Je suppose donc que c’est moi qui fait quelque chose de mal.

Cela ressemble beaucoup à convertit le résultat Hibernate en une liste d'objets , mais ici je n'ai pas d'erreur "dure" (le système sait taper Foo, et je n'utilise pas SQLQuery mais une requête directe) . Donc pas de joie.

J'ai aussi regardé Exception de classe Hibernate car cela semblait prometteur, mais je me suis rendu compte que je ne pas reçois réellement Exception ... mon problème est simplement un avertissement - un style de codage, si vous voulez.

La documentation sur jboss.org, les manuels d'Hibernate et plusieurs tutoriels ne semblent pas couvrir le sujet dans de tels détails (ou je n'ai pas cherché aux bons endroits?). Lorsqu'ils entrent dans les détails, ils utilisent le casting à la volée - et cela sur des tutoriels qui ne figuraient pas sur le site officiel de jboss.org, alors je suis un peu méfiant.

Le code, une fois compilé, fonctionne sans problème apparent ... que je connaisse ... pour le moment; et les résultats sont ceux attendus.

Alors: est-ce que je le fais bien? Est-ce que je manque quelque chose d'évident? Existe-t-il un moyen "officiel" ou "recommandé" de le faire ?

78
LSerni

Réponse courte @SuppressWarnings est la bonne façon de faire.

Réponse longue, Hibernate renvoie un List brut à partir du Query.list méthode, voir ici . Ce n'est pas un bogue avec Hibernate ou quelque chose qui peut être résolu, le type renvoyé par la requête est inconnu à la compilation.

Donc quand tu écris

final List<MyObject> list = query.list();

Vous effectuez un casting non sécurisé de List à List<MyObject> - cela ne peut être évité.

Il n’ya aucun moyen de réaliser le casting en toute sécurité car le List pourrait contenir quoi que ce soit.

Le seul moyen de faire disparaître l'erreur est encore plus moche

final List<MyObject> list = new LinkedList<>();
for(final Object o : query.list()) {
    list.add((MyObject)o);
}
94
Boris the Spider

La solution consiste à utiliser TypedQuery à la place. Lorsque vous créez une requête à partir de EntityManager, appelez-la comme suit:

TypedQuery<[YourClass]> query = entityManager.createQuery("[your sql]", [YourClass].class);
List<[YourClass]> list = query.getResultList(); //no type warning

Cela fonctionne également de la même manière pour les requêtes nommées, les requêtes nommées natives, etc. Les méthodes correspondantes portent les mêmes noms que celles qui renverraient la requête Vanilla. Utilisez simplement ceci au lieu d’une requête chaque fois que vous connaissez le type de retour.

20
Taugenichts

Vous pouvez éviter les avertissements du compilateur avec des solutions comme celle-ci:

List<?> resultRaw = query.list();
List<MyObj> result = new ArrayList<MyObj>(resultRaw.size());
for (Object o : resultRaw) {
    result.add((MyObj) o);
}

Mais il y a quelques problèmes avec ce code:

  • créé ArrayList superflu
  • boucle inutile sur tous les éléments renvoyés par la requête
  • code plus long.

Et la différence n’est que cosmétique, donc utiliser de telles solutions de contournement est - à mon avis - inutile.

Vous devez vivre avec ces avertissements ou les supprimer.

6

Pour répondre à votre question, il n'y a pas de "bonne manière" de le faire. Maintenant, s'il ne s'agit que de l'avertissement qui vous dérange, le meilleur moyen d'éviter sa prolifération consiste à envelopper la méthode Query.list() dans un DAO:

public class MyDAO {

    @SuppressWarnings("unchecked")
    public static <T> List<T> list(Query q){
        return q.list();
    }
}

De cette façon, vous n'utiliserez la @SuppressWarnings("unchecked") qu'une seule fois.

3
Pdv
List<Person> list = new ArrayList<Person>();
Criteria criteria = this.getSessionFactory().getCurrentSession().createCriteria(Person.class);
for (final Object o : criteria.list()) {
    list.add((Person) o);
}
3
user3184564

La seule façon de travailler pour moi était avec un Iterator.

Iterator iterator= query.list().iterator();
Destination dest;
ArrayList<Destination> destinations= new ArrayList<>();
Iterator iterator= query.list().iterator();
    while(iterator.hasNext()){
        Object[] Tuple= (Object[]) iterator.next();
        dest= new Destination();
        dest.setId((String)Tuple[0]);
        dest.setName((String)Tuple[1]);
        dest.setLat((String)Tuple[2]);
        dest.setLng((String)Tuple[3]);
        destinations.add(dest);
    }

Avec d'autres méthodes que j'ai trouvées, j'avais des problèmes de plâtre

3
Popa Andrei

Vous utilisez un ResultTransformer comme ça:

public List<Foo> activeObjects() {
    Session s = acquireSession();
    Query   q = s.createQuery("from foo where active");
    q.setResultTransformer(Transformers.aliasToBean(Foo.class));
    return (List<Foo>) q.list();
}
2
lakreqta

Juste juste en utilisant Transformers Cela n'a pas fonctionné pour moi, j'ai eu une exception de transtypage.

sqlQuery.setResultTransformer(Transformers.aliasToBean(MYEngityName.class)) n'a pas travaillé parce que je récupérais Array of Object dans l'élément de liste de retour et non dans le type d'élément de liste MYEngityName fixé.

Cela a fonctionné pour moi lorsque je fais les modifications suivantes. Après avoir ajouté sqlQuery.addScalar(-) chaque colonne sélectionnée et son type, il n'est pas nécessaire de mapper son type pour une colonne de type String spécifique. comme addScalar("langCode");

Et j'ai rejoint MYEngityName avec NextEnity, nous ne pouvons pas simplement select * dans la requête, il donnera un tableau d'objets dans la liste des retours.

Exemple de code ci-dessous:

session = ht.getSessionFactory().openSession();
                String sql = new StringBuffer("Select txnId,nft.mId,count,retryReason,langCode FROM  MYEngityName nft INNER JOIN NextEntity m on nft.mId  =  m.id where nft.txnId < ").append(lastTxnId)
                       .append(StringUtils.isNotBlank(regionalCountryOfService)? " And  m.countryOfService in ( "+ regionalCountryOfService +" )" :"")
                       .append(" order by nft.txnId desc").toString();
                SQLQuery sqlQuery = session.createSQLQuery(sql);
                sqlQuery.setResultTransformer(Transformers.aliasToBean(MYEngityName.class));
                sqlQuery.addScalar("txnId",Hibernate.LONG)
                        .addScalar("merchantId",Hibernate.INTEGER)
                        .addScalar("count",Hibernate.BYTE)
                        .addScalar("retryReason")
                        .addScalar("langCode");
                sqlQuery.setMaxResults(maxLimit);
                return sqlQuery.list();

Cela pourrait aider quelqu'un. de cette façon travaillent pour moi.

0
Laxman G

La bonne façon consiste à utiliser Hibernate Transformers:

public class StudentDTO {
private String studentName;
private String courseDescription;

public StudentDTO() { }  
...
} 

.

List resultWithAliasedBean = s.createSQLQuery(
"SELECT st.name as studentName, co.description as courseDescription " +
"FROM Enrolment e " +
"INNER JOIN Student st on e.studentId=st.studentId " +
"INNER JOIN Course co on e.courseCode=co.courseCode")
.setResultTransformer( Transformers.aliasToBean(StudentDTO.class))
.list();

StudentDTO dto =(StudentDTO) resultWithAliasedBean.get(0);

Itérer dans Object [] est redondant et entraînerait une pénalité de performance. Vous trouverez des informations détaillées sur l’utilisation des transofrmers ici: Transformers for HQL et SQL

Si vous recherchez une solution encore plus simple, vous pouvez utiliser un transformateur de carte prêt à l'emploi:

List iter = s.createQuery(
"select e.student.name as studentName," +
"       e.course.description as courseDescription" +
"from   Enrolment as e")
.setResultTransformer( Transformers.ALIAS_TO_ENTITY_MAP )
.iterate();

String name = (Map)(iter.next()).get("studentName");
0
ANTARA