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

StackOverflow https://stackoverflow.com/questions/115692

  •  02-07-2019
  •  | 
  •  

Domanda

Ad esempio ho una domanda del genere:

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

Se provo a creare qualcosa del genere mostra il seguente avvertimento

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?

È stato utile?

Soluzione

L'uso di @SuppressWarnings ovunque, come suggerito, è un buon modo per farlo, anche se comporta la digitazione di un dito ogni volta che chiami q.list().

Ci sono altre due tecniche che suggerirei:

Scrivi un cast helper

Rifattorizza semplicemente tutti i tuoi Ignore unavoidable generic type problems due to raw APIs in 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;
}

Impedisci a Eclipse di generare avvisi per problemi inevitabili

In Eclipse, vai su Window > Preferenze > Java > Compilatore > Errori / Avvertenze e in Tipo generico, seleziona la casella di controllo Query

Questo disattiverà avvertimenti non necessari per problemi simili come quello sopra descritto che sono inevitabili.

Alcuni commenti:

  • Ho scelto di inserire List invece del risultato di .iterate() perché in questo modo " barare " il metodo può essere utilizzato solo per imbrogliare con Hibernate e non per barare qualsiasi <=> in generale.
  • Potresti aggiungere metodi simili per <=> ecc.

Altri suggerimenti

È 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 a javax.persistence api docs , vedrai che alcuni nuovi metodi sono stati aggiunti lì da Java Persistence 2.0. Uno di questi è createQuery(String, Class<T>) che restituisce TypedQuery<T>. Puoi utilizzare TypedQuery proprio come hai fatto con Query con quella piccola differenza che tutte le operazioni sono sicure al momento.

Quindi, basta cambiare il codice in smth in questo modo:

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

E sei pronto.

Usiamo anche @SuppressWarnings("unchecked"), ma molto spesso proviamo ad 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;
}

Prova a usare TypedQuery invece 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();

Nel nostro codice annotiamo i metodi di chiamata con:

@SuppressWarnings (quot &; & Incontrollato quot;)

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

Apparentemente, il metodo Query.list () nell'API Hibernate non è sicuro " in base alla progettazione " ;, e ci sono non prevede di cambiarlo .

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

Se hai un metodo che incapsula Query.list () e restituisce List (o Collection), ricevi anche un avviso. Ma questo viene soppresso usando @SuppressWarnings (& Quot; rawtypes & Quot;).

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

Verrà visualizzato un errore di compilazione: Tipo non corrispondente: impossibile convertire dall'elenco all'arrayList

Non è una svista o un errore. L'avvertimento riflette un vero problema di fondo: non c'è modo in cui il compilatore java possa davvero essere sicuro che la classe di ibernazione farà il suo lavoro correttamente e che l'elenco che restituirà conterrà solo gatti. Tutti i suggerimenti qui vanno bene.

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

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

In particolare:

  1. controlla quando è possibile eseguire il commit di una transazione. (volevamo contare quante volte un tx era " iniziato " e commettevamo solo quando il tx era " terminato " lo stesso numero di volte in cui è stato avviato. per il codice che non sa se è necessario avviare una transazione. Ora qualsiasi codice che necessita di un tx è solo " avvia " uno e termina al termine.)
  2. Raccolta delle metriche delle prestazioni.
  3. Ritardare l'avvio della transazione fino a quando non si sa che verrà effettivamente fatto qualcosa.
  4. Comportamento più delicato per query.uniqueResult ()

Quindi per noi abbiamo:

  1. Crea un'interfaccia (AmplafiQuery) che estende la query
  2. Crea una classe (AmplafiQueryImpl) che estende AmplafiQuery e racchiude un org.hibernate.Query
  3. Crea un Txmanager che restituisce 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 & Quot; unique () & Quot; che è una versione abilitata generica di Query.uniqueResult () (e registra solo un problema anziché generare un'eccezione)

Questo è molto lavoro per evitare @SuppressWarnings. Tuttavia, come ho detto (e elencato) ce ne sono molti altri migliori! motivi per eseguire il lavoro di wrapping.

Le versioni più recenti di Hibernate ora supportano un oggetto Query<T> di tipo sicuro, quindi non è più necessario utilizzare @SuppressWarnings o implementare alcuni hack per far scomparire gli avvisi del compilatore. Nel API sessione , Session.createQuery ora restituirà un oggetto <=> sicuro. 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();
}

La soluzione di Joe Dean sembra interessante, ma pensi che ne valga la pena: creare una nuova lista e scorrere tutti gli elementi solo per sbarazzarti degli avvertimenti?

(scusate, non posso aggiungere un commento direttamente alla sua soluzione per qualche motivo)

So che questo è più vecchio ma 2 punti da notare ad 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 questo

List list = q.list();

a questo

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

ridurrebbe ovviamente altri avvisi nei marcatori dei tag di risposta originali rimossi dal browser.

Prova questo:

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

Una buona soluzione per evitare avvisi di sicurezza del tipo con query di ibernazione è quella di utilizzare uno strumento come TorpedoQuery per aiutarti a costruire il tipo hql sicuro.

Cat cat = from(Cat.class);
org.torpedoquery.jpa.Query<Entity> select = select(cat);
List<Cat> cats = select.list(entityManager);
TypedQuery<EntityName> createQuery = entityManager.createQuery("from EntityName", EntityName.class);
List<EntityName> resultList = createQuery.getResultList();

Se non si desidera utilizzare @SuppressWarnings (" non selezionato ") è possibile effettuare le seguenti operazioni.

   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 lo fa per me in modo da non sporcare il mio codice e non devo usare @SupressWarning.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top