Wie vermeide ich Typ-Sicherheitswarnungen mit Hibernate HQL-Ergebnissen?

105

Zum Beispiel habe ich eine solche Abfrage:

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

Wenn ich versuche, so etwas zu machen, wird die folgende Warnung angezeigt

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


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

Gibt es eine Möglichkeit, dies zu vermeiden?

serg
quelle
11
Es ist erwähnenswert, dass Sie mit JPA typsichere Abfragen durchführen können, indem Sie den Typ zur createQuery hinzufügen.
Elazar Leibovich
5
Ein bisschen spät, aber sess.createQuery("from Cat cat", Cat.class);wie Elazar erwähnte.
Dominik Mohr

Antworten:

99

Unter Verwendung @SuppressWarningsüberall, wie vorgeschlagen, ist ein guter Weg , es zu tun, obwohl es ein wenig Fingers mit sich bringt jedes Mal eingeben Sie anrufen q.list().

Es gibt zwei andere Techniken, die ich vorschlagen würde:

Schreiben Sie einen Cast-Helfer

Refaktorieren Sie einfach alles @SuppressWarningsan einem Ort:

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

...

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

Verhindern Sie, dass Eclipse Warnungen für unvermeidbare Probleme generiert

Gehen Sie in Eclipse zu Fenster> Einstellungen> Java> Compiler> Fehler / Warnungen und aktivieren Sie unter Generischer Typ das Kontrollkästchen Ignore unavoidable generic type problems due to raw APIs

Dadurch werden unnötige Warnungen für ähnliche Probleme wie das oben beschriebene deaktiviert, die unvermeidbar sind.

Einige Kommentare:

  • Ich habe mich dafür entschieden, Queryanstelle des Ergebnisses von zu übergeben, q.list()weil auf diese Weise diese "Betrugs" -Methode nur zum Betrügen mit dem Ruhezustand verwendet werden kann und nicht zum Betrügen Listim Allgemeinen.
  • Sie können ähnliche Methoden für .iterate()usw. hinzufügen .
Matt Wachtel
quelle
20
Auf den ersten Blick scheint die Methode Collections.checkedList (Collection <E>, Class <E>) die perfekte Lösung zu sein. Das Javadoc sagt jedoch, dass es nur verhindert, dass falsch typisierte Elemente über die von der Methode generierte typsichere Ansicht hinzugefügt werden. Die angegebene Liste wird nicht überprüft.
Phatblat
11
"List <Cat> list = Collections.checkedList (q.list (), Cat.class);" erfordert weiterhin ein "@SuppressWarnings" in Eclipse. Über den anderen Tipp: Die Eingabe von "listAndCast" ist nicht wirklich kürzer als "@SuppressWarnings", das automatisch über Eclipse hinzugefügt wird.
Tristan
2
Übrigens, die Collections.checkedList()Methode unterdrückt die ungeprüfte Zuweisungswarnung nicht.
Diablo
39

Es ist lange her, dass die Frage gestellt wurde, aber ich hoffe, meine Antwort könnte für jemanden wie mich hilfreich sein.

Wenn Sie sich die API-Dokumente von javax.persistence ansehen , werden Sie feststellen , dass seitdem einige neue Methoden hinzugefügt wurden Java Persistence 2.0. Eine davon ist createQuery(String, Class<T>)die, die zurückkehrt TypedQuery<T>. Sie können TypedQuerygenau so verwenden, wie Sie es getan haben, mit Querydem kleinen Unterschied, dass alle Vorgänge jetzt typsicher sind.

Ändern Sie einfach Ihren Code wie folgt:

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

Und Sie sind fertig.

antonpp
quelle
1
Frage ist nicht über JPA
Mathijs Segers
2
Neuere Versionen von Hibernate implementieren JPA 2.x, daher ist diese Antwort relevant.
Caspinos
TypedQuery <T> ist das beste Szenario.
Muneeb Mirza
21

Wir verwenden es @SuppressWarnings("unchecked")auch, aber wir versuchen meistens, es nur für die Deklaration der Variablen zu verwenden, nicht für die Methode als Ganzes:

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

Versuchen Sie es TypedQueryanstelle von Query. Zum Beispiel stattdessen: -

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

Benutze das:-

TypedQuery<Cat> q1 = sess.createQuery("from Cat cat", Cat.class);
List<Cat> cats = q1.list();
Shivam Oberoi
quelle
1
Gibt es eine Möglichkeit, dies zu tun Criteria?
Stealth Rabbi
5

In unserem Code kommentieren wir die aufrufenden Methoden mit:

@ SuppressWarnings ("nicht markiert")

Ich weiß, es scheint ein Hack zu sein, aber ein Mitentwickler hat kürzlich nachgesehen und festgestellt, dass dies alles war, was wir tun konnten.

Tyshock
quelle
5

Anscheinend ist die Query.list () -Methode in der Hibernate-API "von Entwurf" nicht typsicher, und es gibt keine Pläne, sie zu ändern .

Ich glaube, die einfachste Lösung, um Compiler-Warnungen zu vermeiden, besteht darin, @SuppressWarnings hinzuzufügen ("nicht markiert"). Diese Annotation kann auf Methodenebene oder innerhalb einer Methode direkt vor einer Variablendeklaration platziert werden.

Wenn Sie eine Methode haben, die Query.list () kapselt und List (oder Collection) zurückgibt, erhalten Sie auch eine Warnung. Dieser wird jedoch mit @SuppressWarnings ("rawtypes") unterdrückt.

Die von Matt Quail vorgeschlagene listAndCast (Query) -Methode ist weniger flexibel als Query.list (). Während ich tun kann:

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

Wenn ich den folgenden Code versuche:

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

Ich erhalte einen Kompilierungsfehler: Typ stimmt nicht überein: Konvertiert nicht von List nach ArrayList

Paulo Merson
quelle
1
"Es gibt keine Pläne, es zu ändern." - das ist ein Beitrag aus dem Jahr 2005. Ich wäre überrascht, wenn sich die Dinge seitdem nicht geändert hätten.
Rup
4

Es ist kein Versehen oder ein Fehler. Die Warnung spiegelt ein echtes zugrunde liegendes Problem wider - der Java-Compiler kann auf keinen Fall wirklich sicher sein, dass die Klasse im Ruhezustand ihre Aufgabe ordnungsgemäß erfüllt und dass die zurückgegebene Liste nur Katzen enthält. Jeder der Vorschläge hier ist in Ordnung.

Paulmurray
quelle
2

Nein, aber Sie können es in bestimmte Abfragemethoden isolieren und die Warnungen mit einer @SuppressWarnings("unchecked")Anmerkung unterdrücken .

Dave L.
quelle
Falsch ... Joe Dean hat recht, können Sie die verwenden? als generischer Typ, um die Warnungen zu vermeiden ...
Mike Stone
1
Das ist nicht wahr. Wenn Sie eine Liste <?> Verwenden, können Sie die Elemente der Liste nicht als Cat-Elemente verwenden, ohne den unnötigen Schritt, eine doppelte Liste zu erstellen und jedes Element zu wirken.
Dave L.
Wenn Sie die Ergebnisse direkt über das Casting verwenden, müssen Sie die Liste nicht erstellen. Unabhängig davon lautete die Frage "Gibt es einen Weg, dies zu vermeiden?". Die Antwort lautet definitiv JA (auch ohne Unterdrückungswarnungen)
Mike Stein
2

Neuere Versionen von Hibernate unterstützen jetzt ein typsicheres Query<T>Objekt, sodass Sie keinen @SuppressWarningsHack mehr verwenden oder implementieren müssen, damit die Compiler-Warnungen verschwinden. In der Session - API , Session.createQuerywird nun eine Art sichere Rückkehr Query<T>Objekt. Sie können es folgendermaßen verwenden:

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

Sie können es auch verwenden, wenn das Abfrageergebnis keine Katze zurückgibt:

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

Oder wenn Sie eine Teilauswahl treffen:

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

Wir hatten das gleiche Problem. Aber es war keine große Sache für uns, weil wir andere größere Probleme mit Hibernate Query and Session lösen mussten.

Speziell:

  1. Kontrolle, wann eine Transaktion festgeschrieben werden könnte. (Wir wollten zählen, wie oft ein Sendevorgang "gestartet" wurde, und nur festschreiben, wenn der Sendevorgang genauso oft "beendet" wurde, wie oft er gestartet wurde. Nützlich für Code, der nicht weiß, ob er eine Transaktion starten muss. Jetzt Jeder Code, der einen TX benötigt, "startet" einfach einen und beendet ihn, wenn er fertig ist.)
  2. Erfassung von Leistungsmetriken.
  3. Verzögern Sie den Start der Transaktion, bis bekannt ist, dass tatsächlich etwas getan wird.
  4. Sanfteres Verhalten für query.uniqueResult ()

Für uns haben wir also:

  1. Erstellen Sie eine Schnittstelle (AmplafiQuery), die Query erweitert
  2. Erstellen Sie eine Klasse (AmplafiQueryImpl), die AmplafiQuery erweitert und eine org.hibernate.Query umschließt
  3. Erstellen Sie einen Txmanager, der einen Tx zurückgibt.
  4. Tx verfügt über die verschiedenen createQuery-Methoden und gibt AmplafiQueryImpl zurück

Und zuletzt,

AmplafiQuery hat eine "asList ()", eine generisch aktivierte Version von Query.list (). AmplafiQuery hat eine "unique ()", eine generisch aktivierte Version von Query.uniqueResult () (und protokolliert nur ein Problem, anstatt eine auszulösen Ausnahme)

Dies ist eine Menge Arbeit, um @SuppressWarnings zu vermeiden. Wie ich bereits sagte (und auflistete), gibt es jedoch viele andere bessere! Gründe für die Verpackungsarbeit.

Klopfen
quelle
0

Ich weiß, dass dies älter ist, aber ab heute sind in Matt Quails Answer 2 Punkte zu beachten.

Punkt 1

Dies

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

Sollte das sein

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

Punkt 2

Davon

List list = q.list();

dazu

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

würde andere Warnungen reduzieren, die offensichtlich in den ursprünglichen Antwort-Tag-Markierungen vom Browser entfernt wurden.

Tony Shih
quelle
Versuchen Sie, Antworten zu einer Antwort auf die Frage zu machen, nicht zu einer Antwort auf eine andere Antwort. Es ist in Ordnung, einen Kommentar zu Matt Quails Antwort hinzuzufügen, um zu sagen, dass er veraltet ist, aber schreiben Sie Ihre Antwort einfach rein und korrekt.
Cory Kendall
-1

Versuche dies:

Query q = sess.createQuery("from Cat cat");
List<?> results = q.list();
for (Object obj : results) {
    Cat cat = (Cat) obj;
}
Brian Ngure
quelle
4
Dies ist eine schlechte Kopie von Joe Deans Antwort , da Sie noch etwas mit der catInstanz tun müssen .
Artjom B.
-1

Eine gute Lösung, um Warnungen vor Typensicherheit bei Abfragen im Ruhezustand zu vermeiden, ist die Verwendung eines Tools wie TorpedoQuery, mit dem Sie typsichere HQL erstellen können.

Cat cat = from(Cat.class);
org.torpedoquery.jpa.Query<Entity> select = select(cat);
List<Cat> cats = select.list(entityManager);
Xjodoin
quelle
-1
TypedQuery<EntityName> createQuery = entityManager.createQuery("from EntityName", EntityName.class);
List<EntityName> resultList = createQuery.getResultList();
Rakesh Singh Balhara
quelle
3
Bitte versuchen Sie eine schöne Beschreibung der Funktionsweise Ihrer Lösung zu geben. Siehe: Wie schreibe ich eine gute Antwort? . Vielen Dank.
Shree
1
Können Sie Ihrem Code eine Erklärung hinzufügen, damit andere daraus lernen können?
Nico Haase
-6

Wenn Sie @SuppressWarnings nicht verwenden möchten ("nicht markiert"), können Sie Folgendes tun.

   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);
    }

Zu Ihrer Information - Ich habe eine util-Methode erstellt, die dies für mich erledigt, damit mein Code nicht verschmutzt wird und ich @SupressWarning nicht verwenden muss.

Joe Dean
quelle
2
Das ist einfach nur dumm. Sie fügen Laufzeit-Overhead hinzu, um ein vollständig compilerbezogenes Problem zu beheben. Denken Sie daran, dass Typargumente nicht bestätigt werden, sodass der Typ nicht zur Laufzeit überprüft wird.
John Nilsson
Einverstanden, wenn Sie noch so etwas tun möchten, können Sie eine Laufzeitprüfung des Typs hinzufügen mit: List <Cat> cat = Collections.checkedList (new ArrayList <Cat> (), Cat.class); cat.addAll (q.list ()); Das sollte funktionieren.
ddcruver