web-dev-qa-db-fra.com

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

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, il montre l'avertissement suivant

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


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

Existe-t-il un moyen de l'éviter?

101
serg

L'utilisation de @SuppressWarnings Partout, comme suggéré, est une bonne façon de le faire, même si cela implique un peu de frappe à chaque fois que vous appelez q.list().

Il y a deux autres techniques que je suggérerais:

Écrivez un assistant de casting

Refactorisez simplement tous vos @SuppressWarnings En 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êche Eclipse de générer des avertissements pour des problèmes inévitables

Dans Eclipse, accédez à 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 comme celui décrit ci-dessus qui sont inévitables.

Certains commentaires:

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

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 à javax.persistence api docs , vous verrez que de nouvelles méthodes y ont été ajoutées depuis Java Persistence 2.0. L'un d'eux est createQuery(String, Class<T>) qui renvoie TypedQuery<T>. Vous pouvez utiliser TypedQuery comme vous l'avez fait avec Query avec cette petite différence que toutes les opérations sont désormais sécurisées.

Donc, 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.

33
antonpp

Nous utilisons également @SuppressWarnings("unchecked"), mais nous essayons le plus souvent de ne l'utiliser que 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;
}
21
cretzel

Essayez d'utiliser TypedQuery au lieu de Query. Par exemple au lieu de cela: -

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();
13
shivam oberoi

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 découvert que c'était tout ce que nous pouvions faire.

5
tyshock

Apparemment, la méthode Query.list () dans l'API Hibernate n'est pas sécurisée de type "par conception", et il y a pas de plans pour la changer .

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

Dans le cas où vous avez une méthode qui encapsule Query.list () et renvoie List (ou Collection), vous obtenez également un avertissement. Mais celui-ci est supprimé à l'aide de @SuppressWarnings ("rawtypes").

La méthode listAndCast (Query) proposée par Matt Quail est moins flexible que Query.list (). Alors que je peux 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);

Je vais obtenir une erreur de compilation: Non-concordance de type: impossible de convertir de List en ArrayList

5
Paulo Merson

Ce n'est pas un oubli ou une erreur. L'avertissement reflète un véritable 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.

4
paulmurray

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

2
Dave L.

Les nouvelles versions d'Hibernate prennent désormais en charge un type sécurisé Query<T> objet pour que vous n'ayez plus à utiliser @SuppressWarnings ou implémentez un hack pour faire disparaître les avertissements du compilateur. Dans API de session , Session.createQuery renverra maintenant un type sécurisé Query<T> objet. 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
David DeMar

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 pourrait être validée. (nous voulions compter le nombre de fois qu'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 des 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 Query
  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 retourne 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 simplement @SuppressWarnings. Cependant, comme je l'ai dit (et énuméré), il y a beaucoup d'autres meilleurs! raisons de faire le travail d'emballage.

1
Pat

La solution de Joe Dean semble intéressante, mais pensez-vous que cela en vaut la peine - créez une nouvelle liste et parcourez tous les éléments juste pour vous débarrasser des avertissements?

(désolé, impossible d'ajouter un commentaire directement à sa solution pour une raison quelconque)

0
serg

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

Point 1

Cette

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

Devrait être ceci

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 les autres avertissements dans les marqueurs de balises de réponse d'origine qui ont été supprimés par le navigateur.

0
Tony Shih