Comment éviter les avertissements de sécurité de type avec les résultats Hibernate HQL?


105

Par exemple, j'ai une telle requête:

Query q = sess.createQuery("from Cat cat");
List cats = q.list();

Si j'essaye de faire quelque chose comme ça, ça affiche l'avertissement suivant

Type safety: The expression of type List needs unchecked conversion to conform to List<Cat>


List<Cat> cats = q.list();

Y a-t-il un moyen de l'éviter?


11
Il convient de mentionner qu'avec JPA, vous pouvez avoir des requêtes de type sécurisé, en ajoutant le type à createQuery.
Elazar Leibovich

5
Un peu tard, mais sess.createQuery("from Cat cat", Cat.class);comme Elazar l'a mentionné.
Dominik Mohr

Réponses:


99

Utiliser @SuppressWarningspartout, comme suggéré, est un bon moyen de le faire, même si cela implique un peu de frappe du doigt chaque fois que vous appelez q.list().

Il y a deux autres techniques que je suggère:

Ecrire un assistant de casting

Refactorisez simplement tout votre @SuppressWarningsen un seul endroit:

List<Cat> cats = MyHibernateUtils.listAndCast(q);

...

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

Empêcher Eclipse de générer des avertissements pour des problèmes inévitables

Dans Eclipse, allez dans Fenêtre> Préférences> Java> Compilateur> Erreurs / Avertissements et sous Type générique, cochez la case Ignore unavoidable generic type problems due to raw APIs

Cela désactivera les avertissements inutiles pour des problèmes similaires tels que celui décrit ci-dessus qui sont inévitables.

Certains commentaires:

  • J'ai choisi de passer le Queryau lieu du résultat de q.list()parce que de cette façon, cette méthode de "triche" ne peut être utilisée que pour tricher avec Hibernate, et non pour tricher Listen général.
  • Vous pouvez ajouter des méthodes similaires pour .iterate()etc.

20
À première vue, la méthode Collections.checkedList (Collection <E>, Class <E>) ressemble à la solution parfaite. Cependant, le javadoc indique qu'il empêche uniquement les éléments mal typés d'être ajoutés via la vue de type sécurisé que la méthode génère. Aucune vérification n'est effectuée sur la liste donnée.
phatblat

11
"Liste <Cat> list = Collections.checkedList (q.list (), Cat.class);" nécessite toujours un "@SuppressWarnings" dans Eclipse. À propos de l'autre astuce: taper "listAndCast" n'est pas vraiment plus court que "@SuppressWarnings" qui est ajouté automatiquement via Eclipse.
Tristan

2
BTW, la Collections.checkedList()méthode ne surpressera pas l'avertissement d'affectation non cochée.
Diablo

39

Cela fait longtemps que la question n'a pas été posée, mais j'espère que ma réponse pourra être utile à quelqu'un comme moi.

Si vous jetez un œil à la documentation de l' API javax.persistence , vous verrez que de nouvelles méthodes y ont été ajoutées depuis Java Persistence 2.0. L'un d'eux est celui createQuery(String, Class<T>)qui revient TypedQuery<T>. Vous pouvez utiliser TypedQuerycomme vous l'avez fait Queryavec cette petite différence que toutes les opérations sont de type sécurisé maintenant.

Alors, changez simplement votre code en smth comme ceci:

Query q = sess.createQuery("from Cat cat", Cat.class);
List<Cat> cats = q.list();

Et vous êtes prêt.


1
La question ne concerne pas JPA
Mathijs Segers

2
Les versions récentes d'Hibernate implémentent JPA 2.x, donc cette réponse est pertinente.
caspinos

TypedQuery <T> est le meilleur scénario.
Muneeb Mirza

21

Nous l'utilisons @SuppressWarnings("unchecked")aussi, mais nous essayons le plus souvent de l'utiliser uniquement sur la déclaration de la variable, pas sur la méthode dans son ensemble:

public List<Cat> findAll() {
    Query q = sess.createQuery("from Cat cat");
    @SuppressWarnings("unchecked")
    List<Cat> cats = q.list();
    return cats;
}

15

Essayez d'utiliser à la TypedQueryplace de Query. Par exemple au lieu de ceci: -

Query q = sess.createQuery("from Cat cat", Cat.class);
List<Cat> cats = q.list();

Utilisez ceci:-

TypedQuery<Cat> q1 = sess.createQuery("from Cat cat", Cat.class);
List<Cat> cats = q1.list();

1
Y a-t-il un moyen de faire cela avec Criteriacependant?
Stealth Rabbi

5

Dans notre code, nous annotons les méthodes d'appel avec:

@SuppressWarnings ("non coché")

Je sais que cela ressemble à un hack, mais un co-développeur a vérifié récemment et a trouvé que c'était tout ce que nous pouvions faire.


5

Apparemment, la méthode Query.list () dans l'API Hibernate n'est pas sûre de type "par conception", et il n'est pas prévu de la changer .

Je pense que la solution la plus simple pour éviter les avertissements du compilateur est en effet d'ajouter @SuppressWarnings ("non coché"). Cette annotation peut être placée au niveau de la méthode ou, si elle est à l'intérieur d'une méthode, juste avant une déclaration de variable.

Si vous avez une méthode qui encapsule Query.list () et renvoie List (ou Collection), vous obtenez également un avertissement. Mais celui-ci est supprimé en utilisant @SuppressWarnings ("rawtypes").

La méthode listAndCast (Query) proposée par Matt Quail est moins flexible que Query.list (). Bien que je puisse faire:

Query q = sess.createQuery("from Cat cat");
ArrayList cats = q.list();

Si j'essaye le code ci-dessous:

Query q = sess.createQuery("from Cat cat");
ArrayList<Cat> cats = MyHibernateUtils.listAndCast(q);

J'obtiendrai une erreur de compilation: Incompatibilité de type: impossible de convertir de List en ArrayList


1
"il n'est pas prévu de le changer." - c'est un article de 2005. Je serais surpris si les choses n'avaient pas changé depuis.
Rup

4

Ce n'est ni un oubli ni une erreur. L'avertissement reflète un réel problème sous-jacent - il n'y a aucun moyen que le compilateur java puisse vraiment être sûr que la classe hibernate fera son travail correctement et que la liste qu'elle renvoie ne contiendra que des chats. Toutes les suggestions ici sont très bien.


2

Non, mais vous pouvez l'isoler dans des méthodes de requête spécifiques et supprimer les avertissements avec une @SuppressWarnings("unchecked")annotation.


Faux ... Joe Dean a raison, vous pouvez utiliser le? comme type générique pour éviter les avertissements ...
Mike Stone

1
Ce n'est pas vrai. Si vous utilisez une liste <?>, Vous ne pouvez pas utiliser les éléments de la liste comme Cat sans l'étape inutile de créer une liste en double et de lancer chaque élément.
Dave L.

eh bien, si vous utilisez les résultats directement via la diffusion, vous n'avez pas besoin de créer la liste, et peu importe, la question était "y a-t-il un moyen de l'éviter", la réponse est très certainement OUI (même sans avertissements de suppression)
Mike Stone

2

Les versions plus récentes d'Hibernate prennent désormais en charge un Query<T>objet de type sécurisé , vous n'avez donc plus besoin d'utiliser @SuppressWarningsou d'implémenter un hack pour faire disparaître les avertissements du compilateur. Dans l' API de session , Session.createQueryrenvoie désormais un Query<T>objet de type sécurisé . Vous pouvez l'utiliser de cette façon:

Query<Cat> query = session.createQuery("FROM Cat", Cat.class);
List<Cat> cats = query.list();

Vous pouvez également l'utiliser lorsque le résultat de la requête ne renvoie pas de Cat:

public Integer count() {
    Query<Integer> query = sessionFactory.getCurrentSession().createQuery("SELECT COUNT(id) FROM Cat", Integer.class);
    return query.getSingleResult();
}

Ou lors d'une sélection partielle:

public List<Object[]> String getName() {
    Query<Object[]> query = sessionFactory.getCurrentSession().createQuery("SELECT id, name FROM Cat", Object[].class);
    return query.list();
}

1

Nous avons eu le même problème. Mais ce n'était pas un gros problème pour nous car nous devions résoudre d'autres problèmes plus importants avec Hibernate Query and Session.

Plus précisément:

  1. contrôler quand une transaction peut être validée. (nous voulions compter le nombre de fois où un tx a été "démarré" et ne valider que lorsque le tx a été "terminé" le même nombre de fois qu'il a été démarré. Utile pour le code qui ne sait pas s'il doit démarrer une transaction. Maintenant tout code qui a besoin d'un tx en "démarre" un et le termine une fois terminé.)
  2. Collecte de métriques de performance.
  3. Retarder le démarrage de la transaction jusqu'à ce que l'on sache que quelque chose sera réellement fait.
  4. Comportement plus doux pour query.uniqueResult ()

Donc pour nous, nous avons:

  1. Créer une interface (AmplafiQuery) qui étend la requête
  2. Créez une classe (AmplafiQueryImpl) qui étend AmplafiQuery et encapsule un org.hibernate.Query
  3. Créez un Txmanager qui renvoie un Tx.
  4. Tx a les différentes méthodes createQuery et renvoie AmplafiQueryImpl

Et enfin,

AmplafiQuery a un "asList ()" qui est une version générique activée de Query.list () AmplafiQuery a un "unique ()" qui est une version générique activée de Query.uniqueResult () (et enregistre simplement un problème plutôt que de lancer un exception)

C'est beaucoup de travail pour éviter les @SuppressWarnings. Cependant, comme je l'ai dit (et répertorié), il y a beaucoup d'autres meilleurs! raisons de faire le travail d'emballage.


0

Je sais que c'est plus ancien, mais 2 points à noter à partir d'aujourd'hui dans Matt Quails Answer.

Point 1

Ce

List<Cat> cats = Collections.checkedList(Cat.class, q.list());

Devrait être ça

List<Cat> cats = Collections.checkedList(q.list(), Cat.class);

Point 2

De cela

List list = q.list();

pour ça

List<T> list = q.list();

réduirait évidemment d'autres avertissements dans les marqueurs de balise de réponse d'origine supprimés par le navigateur.


Essayez de faire des réponses une réponse à la question, pas une réponse à une autre réponse. C'est bien d'inclure un commentaire sur la réponse de Matt Quail pour dire qu'il n'est plus à jour, mais écrivez simplement votre réponse purement et correctement.
Cory Kendall

-1

Essaye ça:

Query q = sess.createQuery("from Cat cat");
List<?> results = q.list();
for (Object obj : results) {
    Cat cat = (Cat) obj;
}

4
C'est une mauvaise copie de la réponse de Joe Dean , car vous devez encore faire quelque chose avec l' catinstance.
Artjom B.

-1

Une bonne solution pour éviter les avertissements de sécurité de type avec la requête de mise en veille prolongée consiste à utiliser un outil tel que TorpedoQuery pour vous aider à créer un hql de type sécurisé.

Cat cat = from(Cat.class);
org.torpedoquery.jpa.Query<Entity> select = select(cat);
List<Cat> cats = select.list(entityManager);

-1
TypedQuery<EntityName> createQuery = entityManager.createQuery("from EntityName", EntityName.class);
List<EntityName> resultList = createQuery.getResultList();

3
Veuillez essayer de fournir une belle description du fonctionnement de votre solution. Voir: Comment rédiger une bonne réponse? . Merci.
Shree le

1
Pouvez-vous ajouter des explications à votre code afin que d'autres puissent en tirer des leçons?
Nico Haase

-6

Si vous ne souhaitez pas utiliser @SuppressWarnings ("non coché"), vous pouvez faire ce qui suit.

   Query q = sess.createQuery("from Cat cat");
   List<?> results =(List<?>) q.list();
   List<Cat> cats = new ArrayList<Cat>();
   for(Object result:results) {
       Cat cat = (Cat) result;
       cats.add(cat);
    }

FYI - J'ai créé une méthode util qui fait cela pour moi afin qu'elle ne gâche pas mon code et je n'ai pas à utiliser @SupressWarning.


2
C'est juste stupide. Vous ajoutez une surcharge d'exécution pour surmonter un problème entièrement lié au compilateur. N'oubliez pas que les arguments de type ne sont pas réifiés, il n'y a donc pas de vérification d'exécution du type.
John Nilsson

D'accord, si vous vouliez toujours faire quelque chose comme ça, vous pouvez ajouter une vérification de type à l'exécution avec: List <Cat> cats = Collections.checkedList (new ArrayList <Cat> (), Cat.class); chats.addAll (q.list ()); Cela devrait fonctionner.
ddcruver
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.