¿Cómo evitar las advertencias de seguridad de tipo con los resultados de Hibernate HQL?

105

Por ejemplo, tengo esa consulta:

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

Si trato de hacer algo como esto, muestra la siguiente advertencia

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


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

¿Hay alguna forma de evitarlo?

serg
fuente
11
Vale la pena mencionar que con JPA puede tener consultas seguras de tipos, agregando el tipo a createQuery.
Elazar Leibovich
5
Un poco tarde, pero sess.createQuery("from Cat cat", Cat.class);como mencionó Elazar.
Dominik Mohr

Respuestas:

99

Usar en @SuppressWarningstodas partes, como se sugiere, es una buena manera de hacerlo, aunque implica escribir un poco con el dedo cada vez que llama q.list().

Hay otras dos técnicas que sugeriría:

Escribe un ayudante de reparto

Simplemente refactorice todo @SuppressWarningsen un solo lugar:

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

...

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

Evite que Eclipse genere advertencias por problemas inevitables

En Eclipse, vaya a Ventana> Preferencias> Java> Compilador> Errores / Advertencias y en Tipo genérico, seleccione la casilla de verificación Ignore unavoidable generic type problems due to raw APIs

Esto desactivará las advertencias innecesarias para problemas similares como el descrito anteriormente que son inevitables.

Algunos comentarios:

  • Elegí pasar el en Querylugar del resultado de q.list()porque de esa manera este método de "trampa" solo se puede usar para hacer trampa con Hibernate, y no para engañar a nadie Listen general.
  • Puede agregar métodos similares para .iterate()etc.
Matt Quail
fuente
20
A primera vista, el método Collections.checkedList (Collection <E>, Class <E>) parece la solución perfecta. Sin embargo, el javadoc dice que solo evita que se agreguen elementos escritos incorrectamente a través de la vista de seguridad de tipos que genera el método. No se realiza ninguna comprobación en la lista dada.
phatblat
11
"List <Cat> list = Collections.checkedList (q.list (), Cat.class);" todavía requiere un "@SuppressWarnings" en Eclipse. Acerca del otro consejo: escribir "listAndCast" no es realmente más corto que "@SuppressWarnings" que se agrega automáticamente a través de Eclipse.
Tristan
2
Por cierto, el Collections.checkedList()método no suprimirá la advertencia de asignación no marcada.
Diablo
39

Ha pasado mucho tiempo desde que se hizo la pregunta, pero espero que mi respuesta pueda ser útil para alguien como yo.

Si echa un vistazo a los documentos de la API de javax.persistence , verá que desde entonces se han agregado algunos métodos nuevos Java Persistence 2.0. Uno de ellos es el createQuery(String, Class<T>)que vuelve TypedQuery<T>. Puede usarlo TypedQuerytal como lo hizo Querycon esa pequeña diferencia de que ahora todas las operaciones son seguras de tipos.

Entonces, simplemente cambie su código a algo como esto:

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

Y ya está todo listo.

antonpp
fuente
1
La pregunta no es sobre JPA
Mathijs Segers
2
Las versiones recientes de Hibernate implementan JPA 2.x, por lo que esta respuesta es relevante.
caspinos
TypedQuery <T> es el mejor escenario.
Muneeb Mirza
21

También usamos @SuppressWarnings("unchecked"), pero la mayoría de las veces intentamos usarlo solo en la declaración de la variable, no en el método en su conjunto:

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

Intente usar en TypedQuerylugar de Query. Por ejemplo, en lugar de esto: -

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

Utilizar este:-

TypedQuery<Cat> q1 = sess.createQuery("from Cat cat", Cat.class);
List<Cat> cats = q1.list();
shivam oberoi
fuente
1
¿Hay alguna forma de hacer esto Criteria?
Stealth Rabbi
5

En nuestro código anotamos los métodos de llamada con:

@SuppressWarnings ("sin marcar")

Sé que parece un truco, pero un co-desarrollador lo comprobó recientemente y descubrió que era todo lo que podíamos hacer.

tyshock
fuente
5

Aparentemente, el método Query.list () en la API de Hibernate no es seguro para tipos "por diseño", y no hay planes para cambiarlo .

Creo que la solución más simple para evitar las advertencias del compilador es agregar @SuppressWarnings ("sin marcar"). Esta anotación se puede colocar a nivel de método o, si está dentro de un método, justo antes de una declaración de variable.

En caso de que tenga un método que encapsule Query.list () y devuelva List (o Collection), también recibirá una advertencia. Pero este se suprime usando @SuppressWarnings ("rawtypes").

El método listAndCast (Query) propuesto por Matt Quail es menos flexible que Query.list (). Mientras puedo hacer:

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

Si intento el siguiente código:

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

Recibiré un error de compilación: no coinciden los tipos: no se puede convertir de List a ArrayList

Paulo Merson
fuente
1
"No hay planes para cambiarlo". - esa es una publicación de 2005. Me sorprendería que las cosas no hubieran cambiado desde entonces.
Rup
4

No es un descuido ni un error. La advertencia refleja un problema subyacente real: no hay forma de que el compilador de Java pueda estar realmente seguro de que la clase de hibernación va a hacer su trabajo correctamente y que la lista que devuelve solo contendrá Cats. Cualquiera de las sugerencias aquí está bien.

Paulmurray
fuente
2

No, pero puede aislarlo en métodos de consulta específicos y suprimir las advertencias con una @SuppressWarnings("unchecked")anotación.

Dave L.
fuente
Incorrecto ... Joe Dean tiene razón, ¿puedes usar el? como el tipo genérico para evitar las advertencias ...
Mike Stone
1
Eso no es cierto. Si usa una Lista <?>, Entonces no puede usar los elementos de la lista como Cat sin el paso innecesario de crear una lista duplicada y enviar cada elemento.
Dave L.
Bueno, si usa los resultados directamente a través de la transmisión, no necesita crear la lista y, independientemente, la pregunta era "¿hay alguna manera de evitarlo?", la respuesta es definitivamente SÍ (incluso sin las advertencias de supresión)
Mike Stone
2

Las versiones más recientes de Hibernate ahora admiten un Query<T>objeto de tipo seguro para que ya no tenga que usar @SuppressWarningso implementar algún truco para que desaparezcan las advertencias del compilador. En la API de sesión , Session.createQueryahora devolverá un Query<T>objeto de tipo seguro . Puedes usarlo de esta manera:

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

También puede usarlo cuando el resultado de la consulta no devuelva un gato:

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

O al hacer una selección parcial:

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

Tuvimos el mismo problema. Pero no fue un gran problema para nosotros porque teníamos que resolver otros problemas más importantes con Hibernate Query and Session.

Específicamente:

  1. controlar cuándo se puede realizar una transacción. (Queríamos contar cuántas veces se "inició" un tx y solo confirmar cuando el tx se "terminó" la misma cantidad de veces que se inició. Útil para el código que no sabe si necesita iniciar una transacción. Ahora cualquier código que necesite un tx simplemente "inicia" uno y lo finaliza cuando termina).
  2. Recopilación de métricas de desempeño.
  3. Retrasar el inicio de la transacción hasta que se sepa que realmente se hará algo.
  4. Comportamiento más suave para query.uniqueResult ()

Entonces, para nosotros, tenemos:

  1. Cree una interfaz (AmplafiQuery) que amplíe Query
  2. Cree una clase (AmplafiQueryImpl) que amplíe AmplafiQuery y envuelva un org.hibernate.Query
  3. Cree un Txmanager que devuelva un Tx.
  4. Tx tiene los distintos métodos createQuery y devuelve AmplafiQueryImpl

Y por último,

AmplafiQuery tiene un "asList ()" que es una versión genérica habilitada de Query.list () AmplafiQuery tiene un "unique ()" que es una versión genérica habilitada de Query.uniqueResult () (y solo registra un problema en lugar de lanzar un excepción)

Esto es mucho trabajo simplemente para evitar @SuppressWarnings. Sin embargo, como dije (y enumeré), ¡hay muchos otros mejores! razones para hacer el trabajo de envoltura.

Palmadita
fuente
0

Sé que esto es más antiguo, pero hay 2 puntos a tener en cuenta a partir de hoy en Matt Quails Answer.

Punto 1

Esta

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

Debería ser esto

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

Punto 2

De esto

List list = q.list();

a esto

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

reduciría otras advertencias, obviamente en el navegador, los marcadores de etiquetas de respuesta originales fueron eliminados.

Tony Shih
fuente
Trate de hacer que las respuestas sean una respuesta a la pregunta, no una respuesta a otra respuesta. Está bien incluir un comentario sobre la respuesta de Matt Quail para decir que está desactualizado, pero solo escriba su respuesta de manera pura y correcta.
Cory Kendall
-1

Prueba esto:

Query q = sess.createQuery("from Cat cat");
List<?> results = q.list();
for (Object obj : results) {
    Cat cat = (Cat) obj;
}
Brian Ngure
fuente
4
Esta es una mala copia de la respuesta de Joe Dean , porque aún tiene que hacer algo con la catinstancia.
Artjom B.
-1

Una buena solución para evitar advertencias de seguridad de tipos con consultas de hibernación es usar una herramienta como TorpedoQuery para ayudarlo a construir un hql seguro de tipos.

Cat cat = from(Cat.class);
org.torpedoquery.jpa.Query<Entity> select = select(cat);
List<Cat> cats = select.list(entityManager);
xjodoin
fuente
-1
TypedQuery<EntityName> createQuery = entityManager.createQuery("from EntityName", EntityName.class);
List<EntityName> resultList = createQuery.getResultList();
Rakesh Singh Balhara
fuente
3
Intente proporcionar una descripción agradable sobre cómo funciona su solución. Ver: ¿Cómo escribo una buena respuesta? . Gracias.
Shree
1
¿Puede agregar alguna explicación a su código para que otros puedan aprender de él?
Nico Haase
-6

Si no desea utilizar @SuppressWarnings ("sin marcar"), puede hacer lo siguiente.

   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: creé un método util que hace esto por mí para que no ensucie mi código y no tenga que usar @SupressWarning.

Joe Dean
fuente
2
Eso es estúpido. Está agregando sobrecarga de tiempo de ejecución para superar un problema completamente relacionado con el compilador. Recuerde que los argumentos de tipo no están reificados, por lo que no hay verificación en tiempo de ejecución del tipo.
John Nilsson
De acuerdo, si todavía quisiera hacer algo como esto, podría agregar la verificación de tipo en tiempo de ejecución con: List <Cat> cats = Collections.checkedList (new ArrayList <Cat> (), Cat.class); gatos.addAll (q.list ()); Esto debería funcionar.
ddcruver