Soy un novato con Hibernate y estoy escribiendo un método simple para devolver una lista de objetos que coinciden con un filtro específico. List<Foo>
parecía un tipo de retorno natural.
Haga lo que haga, parece que no puedo hacer feliz al compilador, a menos que emplee un archivo feo @SuppressWarnings
.
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
public class Foo {
public Session acquireSession() {
// All DB opening, connection etc. removed,
// since the problem is in compilation, not at runtime.
return null;
}
@SuppressWarnings("unchecked") /* <----- */
public List<Foo> activeObjects() {
Session s = acquireSession();
Query q = s.createQuery("from foo where active");
return (List<Foo>) q.list();
}
}
Me gustaría deshacerme de esoSuppressWarnings
. Pero si lo hago, recibo la advertencia
Warning: Unchecked cast from List to List<Foo>
(Puedo ignorarlo, pero me gustaría no obtenerlo en primer lugar), y si elimino el genérico para cumplir con el .list()
tipo de retorno, aparece la advertencia
Warning: List is a raw type. References to generic type List<E>
should be parameterized.
Noté que org.hibernate.mapping
hace declarar List
; pero es un tipo completamente diferente: Query
devuelve a java.util.List
, como tipo sin formato. Me parece extraño que un Hibernate reciente (4.0.x) no implemente tipos parametrizados, así que sospecho que soy yo, en cambio, haciendo algo mal.
Se parece mucho al resultado de Cast Hibernate a una lista de objetos , pero aquí no tengo errores "duros" (el sistema conoce el tipo Foo, y no estoy usando una SQLQuery sino una consulta directa). Así que no hay alegría.
También he analizado la excepción de conversión de clase de Hibernate, ya que parecía prometedor, pero luego me di cuenta de que en realidad no obtengo ninguna Exception
... mi problema es solo una advertencia, un estilo de codificación, por así decirlo.
La documentación en jboss.org, los manuales de Hibernate y varios tutoriales no parecen cubrir el tema con tanto detalle (¿o no busqué en los lugares correctos?). Cuando entran en detalles, usan la transmisión sobre la marcha, y esto en tutoriales que no estaban en el sitio oficial jboss.org, así que soy un poco cauteloso.
El código, una vez compilado, se ejecuta sin problema aparente ... que yo sepa ... todavía; y los resultados son los esperados.
Entonces: ¿estoy haciendo esto bien? ¿Me estoy perdiendo algo obvio? ¿Existe una forma "oficial" o "recomendada" de hacerlo ?
final
Class<?>
de entradalist()
, el problema podría resolverse. Es una pena usar una API tan fea.List<Object>
. Los resultados se deben convertir al tipo esperado y se deben agregar pruebas unitarias para garantizar que la consulta devuelva los resultados correctos. Es inaceptable que aparezcan errores con consultas " más adelante en el código ". Su ejemplo es un argumento en contra de las prácticas de codificación que deberían ser un anatema en el siglo XXI. Sugeriría que nunca es aceptable tener unList<Object>
.La resolución es utilizar TypedQuery en su lugar. Al crear una consulta desde el EntityManager, llámelo así:
TypedQuery<[YourClass]> query = entityManager.createQuery("[your sql]", [YourClass].class); List<[YourClass]> list = query.getResultList(); //no type warning
Esto también funciona de la misma forma para consultas con nombre, consultas con nombre nativo, etc. Los métodos correspondientes tienen los mismos nombres que los que devolverían la consulta básica. Solo use esto en lugar de una Consulta siempre que sepa el tipo de retorno.
fuente
Puede evitar la advertencia del compilador con soluciones alternativas como esta:
List<?> resultRaw = query.list(); List<MyObj> result = new ArrayList<MyObj>(resultRaw.size()); for (Object o : resultRaw) { result.add((MyObj) o); }
Pero hay algunos problemas con este código:
Y la diferencia es solo cosmética, por lo que usar tales soluciones es, en mi opinión, inútil.
Tienes que vivir con estas advertencias o reprimirlas.
fuente
Para responder a su pregunta, no existe una "forma adecuada" de hacerlo. Ahora bien, si es solo la advertencia lo que le molesta, la mejor manera de evitar su proliferación es envolver el
Query.list()
método en un DAO:public class MyDAO { @SuppressWarnings("unchecked") public static <T> List<T> list(Query q){ return q.list(); } }
De esta manera puedes usar el
@SuppressWarnings("unchecked")
único una vez.fuente
La única forma en que me funcionó fue con un iterador.
Iterator iterator= query.list().iterator(); Destination dest; ArrayList<Destination> destinations= new ArrayList<>(); Iterator iterator= query.list().iterator(); while(iterator.hasNext()){ Object[] tuple= (Object[]) iterator.next(); dest= new Destination(); dest.setId((String)tuple[0]); dest.setName((String)tuple[1]); dest.setLat((String)tuple[2]); dest.setLng((String)tuple[3]); destinations.add(dest); }
Con otros métodos que encontré, tuve problemas de transmisión
fuente
Destinstion
para ti, verdad? Usando laselect new
sintaxis. Este ciertamente no es el enfoque correcto.List<Person> list = new ArrayList<Person>(); Criteria criteria = this.getSessionFactory().getCurrentSession().createCriteria(Person.class); for (final Object o : criteria.list()) { list.add((Person) o); }
fuente
Usas un ResultTransformer como ese:
public List<Foo> activeObjects() { Session s = acquireSession(); Query q = s.createQuery("from foo where active"); q.setResultTransformer(Transformers.aliasToBean(Foo.class)); return (List<Foo>) q.list(); }
fuente
q
sigue siendo un tipo sin formatoQuery
y, porq.list()
lo tanto, sigue siendojava.util.List
. Entonces, el elenco aún no se controla; tener el tipo de objeto cambiado internamente no debería servir de nada ...from foo where active
no es una consulta nativa. Por lo tanto, no es necesario un transformador de resultados, ya que el mapeo predeterminado será suficiente. La pregunta no es sobre la conversión de los campos POJO, sino sobre la conversión del objeto de resultado. Un transformador de resultado no ayudaría aquí.La forma correcta es usar Hibernate Transformers:
public class StudentDTO { private String studentName; private String courseDescription; public StudentDTO() { } ... }
.
List resultWithAliasedBean = s.createSQLQuery( "SELECT st.name as studentName, co.description as courseDescription " + "FROM Enrolment e " + "INNER JOIN Student st on e.studentId=st.studentId " + "INNER JOIN Course co on e.courseCode=co.courseCode") .setResultTransformer( Transformers.aliasToBean(StudentDTO.class)) .list(); StudentDTO dto =(StudentDTO) resultWithAliasedBean.get(0);
Iterar a través de Object [] es redundante y tendría alguna penalización en el rendimiento. Aquí encontrará información detallada sobre el uso de transofrmers: Transformers para HQL y SQL
Si está buscando una solución aún más simple, puede usar el transformador de mapa listo para usar:
List iter = s.createQuery( "select e.student.name as studentName," + " e.course.description as courseDescription" + "from Enrolment as e") .setResultTransformer( Transformers.ALIAS_TO_ENTITY_MAP ) .iterate(); String name = (Map)(iter.next()).get("studentName");
fuente
Query
resultados, lo que todavía es necesario en su ejemplo. Y tu ejemplo no tiene nada que ver con el originalfrom foo where active
.Solo usando Transformers No funcionó para mí, estaba obteniendo una excepción de conversión de tipo.
sqlQuery.setResultTransformer(Transformers.aliasToBean(MYEngityName.class))
no funcionó porque estaba obteniendo Array of Object en el elemento de la lista de retorno, no en el tipo fijo MYEngityName del elemento de lista.Me funcionó cuando hago los siguientes cambios. Cuando agregué
sqlQuery.addScalar(-)
cada columna seleccionada y su tipo y para una columna de tipo de cadena específica, no tenemos que asignar su tipo. me gustaaddScalar("langCode");
Y me he unido a MYEngityName con NextEnity, no podemos solo
select *
en la Consulta, dará una matriz de Objeto en la lista de retorno.Ejemplo de código a continuación:
session = ht.getSessionFactory().openSession(); String sql = new StringBuffer("Select txnId,nft.mId,count,retryReason,langCode FROM MYEngityName nft INNER JOIN NextEntity m on nft.mId = m.id where nft.txnId < ").append(lastTxnId) .append(StringUtils.isNotBlank(regionalCountryOfService)? " And m.countryOfService in ( "+ regionalCountryOfService +" )" :"") .append(" order by nft.txnId desc").toString(); SQLQuery sqlQuery = session.createSQLQuery(sql); sqlQuery.setResultTransformer(Transformers.aliasToBean(MYEngityName.class)); sqlQuery.addScalar("txnId",Hibernate.LONG) .addScalar("merchantId",Hibernate.INTEGER) .addScalar("count",Hibernate.BYTE) .addScalar("retryReason") .addScalar("langCode"); sqlQuery.setMaxResults(maxLimit); return sqlQuery.list();
Podría ayudar a alguien. de esta manera trabaja para mí.
fuente
Encontré la mejor solución aquí , la clave de este problema es el método addEntity
public static void testSimpleSQL() { final Session session = sessionFactory.openSession(); SQLQuery q = session.createSQLQuery("select * from ENTITY"); q.addEntity(Entity.class); List<Entity> entities = q.list(); for (Entity entity : entities) { System.out.println(entity); } }
fuente