Sou um novato no Hibernate e estou escrevendo um método simples para retornar uma lista de objetos que correspondem a um filtro específico. List<Foo>
parecia um tipo de retorno natural.
Faça o que fizer, não consigo deixar o compilador feliz, a menos que eu use um feio @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();
}
}
Eu gostaria de me livrar dissoSuppressWarnings
. Mas se eu fizer isso, recebo o aviso
Warning: Unchecked cast from List to List<Foo>
(Posso ignorá-lo, mas gostaria de não obtê-lo em primeiro lugar), e se eu remover o genérico para estar em conformidade com o .list()
tipo de retorno, recebo o aviso
Warning: List is a raw type. References to generic type List<E>
should be parameterized.
Notei que org.hibernate.mapping
faz declarar um List
; mas é um tipo totalmente diferente - Query
retorna a java.util.List
, como um tipo bruto. Acho estranho que um Hibernate (4.0.x) recente não implementasse tipos parametrizados, então suspeito que sou eu fazendo algo errado.
Parece muito com o resultado do Cast Hibernate para uma lista de objetos , mas aqui não tenho nenhum erro "difícil" (o sistema conhece o tipo Foo, e não estou usando um SQLQuery, mas uma consulta direta). Portanto, nenhuma alegria.
Também examinei Hibernate Class Cast Exception, pois parecia promissor, mas então percebi que não recebo nenhum Exception
... meu problema é apenas um aviso - um estilo de codificação, se preferir.
A documentação no jboss.org, os manuais do Hibernate e vários tutoriais parecem não cobrir o tópico com tantos detalhes (ou não pesquisei nos lugares certos?). Quando eles entram em detalhes, eles usam fundição instantânea - e isso em tutoriais que não estavam no site oficial jboss.org, então estou um pouco desconfiado.
O código, uma vez compilado, é executado sem nenhum problema aparente ... que eu saiba ... ainda; e os resultados são os esperados.
Então: estou fazendo certo? Estou perdendo algo óbvio? Existe uma maneira "oficial" ou "recomendada" de fazer isso ?
final
Class<?>
inlist()
, o problema pode ser resolvido. É uma pena usar uma API tão feia.List<Object>
. Os resultados devem ser convertidos para o tipo esperado e testes de unidade devem ser adicionados para garantir que a consulta retorne os resultados corretos. É inaceitável que erros com consultas apareçam " mais tarde no código ". Seu exemplo é um argumento contra as práticas de codificação que deveriam ser um anátema no século 21. É, eu sugeriria, nunca aceitável ter umList<Object>
.A resolução é usar TypedQuery em vez disso. Ao criar uma consulta no EntityManager, chame-a assim:
TypedQuery<[YourClass]> query = entityManager.createQuery("[your sql]", [YourClass].class); List<[YourClass]> list = query.getResultList(); //no type warning
Isso também funciona da mesma forma para consultas nomeadas, consultas nomeadas nativas, etc. Os métodos correspondentes têm os mesmos nomes que aqueles que retornariam a consulta básica. Basta usar isso em vez de uma consulta sempre que você souber o tipo de retorno.
fonte
Você pode evitar o aviso do compilador com soluções alternativas como esta:
List<?> resultRaw = query.list(); List<MyObj> result = new ArrayList<MyObj>(resultRaw.size()); for (Object o : resultRaw) { result.add((MyObj) o); }
Mas existem alguns problemas com este código:
E a diferença é apenas estética, portanto, usar essas soluções alternativas é - na minha opinião - inútil.
Você tem que conviver com esses avisos ou suprimi-los.
fonte
Para responder à sua pergunta, não existe uma "maneira adequada" de fazer isso. Agora, se é apenas o aviso que o incomoda, a melhor maneira de evitar sua proliferação é envolver o
Query.list()
método em um DAO:public class MyDAO { @SuppressWarnings("unchecked") public static <T> List<T> list(Query q){ return q.list(); } }
Dessa forma, você pode usar
@SuppressWarnings("unchecked")
apenas uma vez.fonte
A única maneira que funcionou para mim foi com um 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); }
Com outros métodos que encontrei, tive problemas de elenco
fonte
Destinstion
para você, certo? Usando aselect new
sintaxe. Essa certamente não é a abordagem certa.List<Person> list = new ArrayList<Person>(); Criteria criteria = this.getSessionFactory().getCurrentSession().createCriteria(Person.class); for (final Object o : criteria.list()) { list.add((Person) o); }
fonte
Você usa um ResultTransformer assim:
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(); }
fonte
q
ainda é umQuery
e, portanto,q.list()
ainda é umjava.util.List
tipo bruto . O elenco ainda está desmarcado; ter o tipo de objeto alterado internamente não deve servir de nada ...from foo where active
não é uma consulta nativa. Portanto, não há necessidade de um transformador de resultado, pois o mapeamento padrão será suficiente. A questão não é sobre a projeção dos campos POJO, mas sobre a projeção do objeto de resultado. Um transformador de resultado não ajudaria aqui.A maneira correta é usar transformadores Hibernate:
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);
A iteração por meio de Object [] é redundante e teria alguma penalidade de desempenho. Informações detalhadas sobre o uso de transformadores você encontrará aqui: Transformers para HQL e SQL
Se você está procurando uma solução ainda mais simples, pode usar o transformador de mapa pronto 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");
fonte
Query
resultados - o que ainda é necessário em seu exemplo. E seu exemplo não tem nada a ver com o originalfrom foo where active
.Apenas usando Transformers Não funcionou para mim, eu estava recebendo uma exceção de elenco de tipo.
sqlQuery.setResultTransformer(Transformers.aliasToBean(MYEngityName.class))
não funcionou porque eu estava obtendo Array of Object no elemento de lista de retorno e não o tipo MYEngityName fixo de elemento de lista.Funcionou para mim quando fiz as seguintes alterações. Quando adicionei
sqlQuery.addScalar(-)
cada coluna selecionada e seu tipo, e para a coluna de tipo String específica, não precisamos mapear seu tipo. gostaraddScalar("langCode");
E eu juntei MYEngityName com NextEnity que não podemos apenas
select *
na Query daremos array de Object na lista de retorno.Exemplo de código abaixo:
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();
Pode ajudar alguém. desta forma funciona para mim.
fonte
Eu encontrei a melhor solução aqui , a chave para este problema é o 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); } }
fonte