Come evitare gli avvisi di sicurezza dei tipi con i risultati di Hibernate HQL?


105

Ad esempio, ho una domanda del genere:

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

Se provo a fare qualcosa di simile, mostra il seguente avviso

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


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

C'è un modo per evitarlo?


11
Vale la pena ricordare che con JPA puoi avere query indipendenti dai tipi, aggiungendo il tipo a createQuery.
Elazar Leibovich

5
Un po 'tardi, ma sess.createQuery("from Cat cat", Cat.class);come ha detto Elazar.
Dominik Mohr

Risposte:


99

Usarlo @SuppressWarningsovunque, come suggerito, è un buon modo per farlo, anche se implica un po 'di digitazione con le dita ogni volta che chiami q.list().

Ci sono altre due tecniche che suggerirei:

Scrivi un cast-helper

Rifattorizza semplicemente tutto @SuppressWarningsin un unico posto:

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

...

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

Impedire a Eclipse di generare avvisi per problemi inevitabili

In Eclipse, vai su Finestra> Preferenze> Java> Compilatore> Errori / Avvisi e in Tipo generico, seleziona la casella di controllo Ignore unavoidable generic type problems due to raw APIs

Ciò disattiverà gli avvisi non necessari per problemi simili come quello descritto sopra che sono inevitabili.

Alcuni commenti:

  • Ho scelto di passare al Queryinvece del risultato di q.list()perché in questo modo questo metodo di "imbroglio" può essere utilizzato solo per barare con Hibernate, e non per barare Listin generale.
  • Potresti aggiungere metodi simili per .iterate()ecc.

20
A prima vista, il metodo Collections.checkedList (Collection <E>, Class <E>) sembra la soluzione perfetta. Tuttavia, il javadoc afferma che impedisce solo l'aggiunta di elementi digitati in modo errato tramite la visualizzazione typesafe generata dal metodo. Non viene eseguito alcun controllo sull'elenco fornito.
phatblat

11
"List <Cat> list = Collections.checkedList (q.list (), Cat.class);" richiede ancora "@SuppressWarnings" in Eclipse. Riguardo l'altro suggerimento: digitare "listAndCast" non è molto più breve di "@SuppressWarnings" che viene aggiunto automaticamente tramite Eclipse.
Tristan

2
A proposito, il Collections.checkedList()metodo non sopprimerà l'avviso di assegnazione deselezionata.
Diablo

39

È passato molto tempo da quando è stata posta la domanda, ma spero che la mia risposta possa essere utile a qualcuno come me.

Se dai un'occhiata alla documentazione api javax.persistence , vedrai che da allora sono stati aggiunti alcuni nuovi metodi Java Persistence 2.0. Uno di questi è createQuery(String, Class<T>)che ritorna TypedQuery<T>. Puoi usarlo TypedQueryesattamente come l'hai fatto Querycon quella piccola differenza che tutte le operazioni sono ora sicure.

Quindi, cambia il tuo codice in questo modo:

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

E sei pronto.


1
La domanda non riguarda JPA
Mathijs Segers

2
Le versioni recenti di Hibernate implementano JPA 2.x, quindi questa risposta è pertinente.
caspinos

TypedQuery <T> è lo scenario migliore.
Muneeb Mirza

21

Lo usiamo @SuppressWarnings("unchecked")anche noi, ma molto spesso cerchiamo di usarlo solo sulla dichiarazione della variabile, non sul metodo nel suo insieme:

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

15

Prova a usare TypedQueryinvece di Query. Ad esempio invece di questo: -

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

Usa questo:-

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

1
C'è un modo per farlo Criteriaperò?
Stealth Rabbi

5

Nel nostro codice annotiamo i metodi chiamanti con:

@SuppressWarnings ( "incontrollato")

So che sembra un hack, ma un co-sviluppatore ha controllato di recente e ha scoperto che era tutto ciò che potevamo fare.


5

Apparentemente, il metodo Query.list () nell'API Hibernate non è sicuro "per impostazione predefinita" e non ci sono piani per cambiarlo .

Credo che la soluzione più semplice per evitare gli avvisi del compilatore sia in effetti aggiungere @SuppressWarnings ("unchecked"). Questa annotazione può essere inserita a livello di metodo o, se all'interno di un metodo, subito prima di una dichiarazione di variabile.

Nel caso in cui si disponga di un metodo che incapsula Query.list () e restituisce List (o Collection), viene visualizzato anche un avviso. Ma questo è soppresso usando @SuppressWarnings ("rawtypes").

Il metodo listAndCast (Query) proposto da Matt Quail è meno flessibile di Query.list (). Mentre posso fare:

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

Se provo il codice qui sotto:

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

Riceverò un errore di compilazione: Type mismatch: cannot convert from List to ArrayList


1
"non ci sono piani per cambiarlo." - questo è un post del 2005. Sarei sorpreso se le cose non fossero cambiate da allora.
Rup

4

Non è una svista o un errore. L'avvertimento riflette un vero problema sottostante: non c'è modo che il compilatore java possa essere veramente sicuro che la classe hibernate farà il suo lavoro correttamente e che l'elenco che restituirà conterrà solo gatti. Qualsiasi suggerimento qui va bene.


2

No, ma puoi isolarlo in metodi di query specifici e sopprimere gli avvisi con @SuppressWarnings("unchecked")un'annotazione.


Sbagliato ... Joe Dean ha ragione, puoi usare il? come tipo generico per evitare gli avvertimenti ...
Mike Stone

1
Non è vero. Se si utilizza un elenco <?>, Non è possibile utilizzare gli elementi dell'elenco come di gatto senza il passaggio non necessario di creare un elenco duplicato e di eseguire il cast di ogni elemento.
Dave L.

beh, se utilizzi i risultati direttamente tramite casting non è necessario creare l'elenco e, a prescindere, la domanda era "c'è un modo per evitarlo", la risposta è decisamente SI (anche senza avvisi di soppressione)
Mike Stone

2

Le versioni più recenti di Hibernate ora supportano un Query<T>oggetto indipendente dai tipi , quindi non devi più usare @SuppressWarningso implementare qualche hack per far sparire gli avvisi del compilatore. Nell'API della sessione , Session.createQueryora restituirà un Query<T>oggetto indipendente dai tipi . Puoi usarlo in questo modo:

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

Puoi anche usarlo quando il risultato della query non restituirà un gatto:

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

O quando si esegue una selezione parziale:

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

1

Abbiamo avuto lo stesso problema. Ma non è stato un grosso problema per noi perché abbiamo dovuto risolvere altri problemi più importanti con Hibernate Query e Session.

In particolare:

  1. controllare quando è possibile eseguire il commit di una transazione. (volevamo contare quante volte una trasmissione è stata "avviata" ed eseguire il commit solo quando la trasmissione è stata "terminata" lo stesso numero di volte in cui è stata avviata. Utile per il codice che non sa se è necessario avviare una transazione. Ora qualsiasi codice che necessita di una trasmissione ne "avvia" uno e lo termina una volta terminato.)
  2. Raccolta delle metriche delle prestazioni.
  3. Ritardare l'avvio della transazione finché non si sa che qualcosa verrà effettivamente fatto.
  4. Comportamento più delicato per query.uniqueResult ()

Quindi per noi abbiamo:

  1. Crea un'interfaccia (AmplafiQuery) che estenda Query
  2. Crea una classe (AmplafiQueryImpl) che estenda AmplafiQuery e racchiuda un org.hibernate.Query
  3. Crea un Txmanager che restituisca un Tx.
  4. Tx ha i vari metodi createQuery e restituisce AmplafiQueryImpl

E infine

AmplafiQuery ha un "asList ()" che è una versione abilitata generica di Query.list () AmplafiQuery ha un "unique ()" che è una versione abilitata generica di Query.uniqueResult () (e registra semplicemente un problema invece di lanciare un eccezione)

Questo è un sacco di lavoro solo per evitare @SuppressWarnings. Tuttavia, come ho detto (ed elencato) ce ne sono molti altri migliori! motivi per fare il lavoro di avvolgimento.


0

So che questo è più vecchio, ma 2 punti da notare a partire da oggi in Matt Quails Answer.

Punto 1

Questo

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

Dovrebbe essere questo

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

Punto 2

Da questa

List list = q.list();

a questa

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

ridurrebbero altri avvisi ovviamente nei marcatori dei tag di risposta originali sono stati rimossi dal browser.


Prova a fare delle risposte una risposta alla domanda, non una risposta a un'altra risposta. Va bene includere un commento alla risposta di Matt Quail per dire che non è aggiornato, ma scrivi la tua risposta in modo puro e corretto.
Cory Kendall

-1

Prova questo:

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

4
Questa è una brutta copia della risposta di Joe Dean , perché devi ancora fare qualcosa con l' catistanza.
Artjom B.

-1

Una buona soluzione per evitare avvisi di sicurezza dei tipi con query di ibernazione è utilizzare uno strumento come TorpedoQuery per aiutarti a creare hql indipendente dai tipi.

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
Prova a fornire una bella descrizione su come funziona la tua soluzione. Vedi: come scrivo una buona risposta? . Grazie.
Shree

1
Puoi aggiungere qualche spiegazione al tuo codice in modo che altri possano imparare da esso?
Nico Haase

-6

Se non vuoi usare @SuppressWarnings ("unchecked") puoi fare quanto segue.

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

Cordiali saluti, ho creato un metodo util che fa questo per me in modo da non sporcare il mio codice e non devo usare @SupressWarning.


2
È semplicemente stupido. Stai aggiungendo un sovraccarico di runtime per superare un problema completamente correlato al compilatore. Ricorda che gli argomenti di tipo non sono reificati, quindi non c'è controllo in fase di esecuzione del tipo.
John Nilsson,

D'accordo, se volevi ancora fare qualcosa del genere potresti aggiungere il controllo di runtime del tipo con: List <Cat> cats = Collections.checkedList (new ArrayList <Cat> (), Cat.class); cats.addAll (q.list ()); Questo dovrebbe funzionare.
ddcruver
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.