'What is the "proper" way to cast Hibernate Query.list() to List<Type>?
I'm a newbie with Hibernate, and I'm writing a simple method to return a list of objects
matching a specific filter. List<Foo>
seemed a natural return type.
Whatever I do, I can't seem to make the compiler happy, unless I employ an ugly @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();
}
}
I would like to get rid of that SuppressWarnings
. But if I do, I get the warning
Warning: Unchecked cast from List to List<Foo>
(I can ignore it, but I'd like to not get it in the first place), and if I remove the generic to conform to .list()
return type, I get the warning
Warning: List is a raw type. References to generic type List<E>
should be parameterized.
I noticed that org.hibernate.mapping
does declare a List
; but it is a different type altogether - Query
returns a java.util.List
, as a raw type. I find it odd that a recent Hibernate (4.0.x) would not implement parameterized types, so I suspect that it's me instead doing something wrong.
It looks very much like Cast Hibernate result to a list of objects, but here I have no "hard" errors (the system knows type Foo, and I'm not using a SQLQuery but a straight Query). So no joy.
I have also looked at Hibernate Class Cast Exception since it looked promising, but then I realized that I do not actually get any Exception
... my problem is just that of a warning - a coding style, if you will.
Documentation on jboss.org, Hibernate manuals and several tutorials do not seem to cover the topic in such detail (or I didn't search in the right places?). When they do enter into detail, they use on-the-fly casting - and this on tutorials that weren't on the official jboss.org site, so I'm a bit wary.
The code, once compiled, runs with no apparent problem... that I know of... yet; and the results are the expected ones.
So: am I doing this right? Am I missing something obvious? Is there an "official" or "recommended" Way To Do It?
Solution 1:[1]
Short answer @SuppressWarnings
is the right way to go.
Long answer, Hibernate returns a raw List
from the Query.list
method, see here. This is not a bug with Hibernate or something the can be solved, the type returned by the query is not known at compile time.
Therefore when you write
final List<MyObject> list = query.list();
You are doing an unsafe cast from List
to List<MyObject>
- this cannot be avoided.
There is no way you can safely carry out the cast as the List
could contain anything.
The only way to make the error go away is the even more ugly
final List<MyObject> list = new LinkedList<>();
for(final Object o : query.list()) {
list.add((MyObject)o);
}
Solution 2:[2]
The resolution is to use TypedQuery instead. When creating a query from the EntityManager instead call it like this:
TypedQuery<[YourClass]> query = entityManager.createQuery("[your sql]", [YourClass].class);
List<[YourClass]> list = query.getResultList(); //no type warning
This also works the same for named queries, native named queries, etc. The corresponding methods have the same names as the ones that would return the vanilla query. Just use this instead of a Query whenever you know the return type.
Solution 3:[3]
To answer your question, there is no "proper way" to do that.
Now if it's just the warning that bothers you, the best way to avoid its proliferation is to wrap the Query.list()
method into a DAO :
public class MyDAO {
@SuppressWarnings("unchecked")
public static <T> List<T> list(Query q){
return q.list();
}
}
This way you get to use the @SuppressWarnings("unchecked")
only once.
Solution 4:[4]
You can avoid compiler warning with workarounds like this one:
List<?> resultRaw = query.list();
List<MyObj> result = new ArrayList<MyObj>(resultRaw.size());
for (Object o : resultRaw) {
result.add((MyObj) o);
}
But there are some issues with this code:
- created superfluous ArrayList
- unnecessary loop over all elements returned from the query
- longer code.
And the difference is only cosmetic, so using such workarounds is - in my opinion - pointless.
You have to live with these warnings or suppress them.
Solution 5:[5]
Only way that work for me was with an Iterator.
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);
}
With other methods that I found, I had cast problems
Solution 6:[6]
List<Person> list = new ArrayList<Person>();
Criteria criteria = this.getSessionFactory().getCurrentSession().createCriteria(Person.class);
for (final Object o : criteria.list()) {
list.add((Person) o);
}
Solution 7:[7]
You use a ResultTransformer like that:
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();
}
Solution 8:[8]
The proper way is to use 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);
Iterating througth Object[] is redundant and would have some performance penalty. Detailed information about transofrmers usage you will find here: Transformers for HQL and SQL
If you are looking for even more simple solution you can use out-of-the-box-map-transformer:
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");
Solution 9:[9]
Just just using Transformers It did not work for me I was getting type cast exception.
sqlQuery.setResultTransformer(Transformers.aliasToBean(MYEngityName.class))
did notwork because I was getting Array of Object in the return list element not the fixed MYEngityName type of list element.
It worked for me when I make following changes When I have added sqlQuery.addScalar(-)
each selected column and its type and for specific String type column we dont have to map its type. like addScalar("langCode");
And I have join MYEngityName with NextEnity we cant just select *
in the Query it will give array of Object in the return list.
Below code sample :
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();
It might help some one. in this way work for me.
Solution 10:[10]
In a project I'm a consultant I had this issue. See the solution:
First, I created the following method:
protected <T> MyTypedQuery<T> createNamedAliasQuery(final String queryName, final Class<T> type) {
final Query q = getSafeEntityManager().createNamedQuery(queryName);
q.unwrap(org.hibernate.Query.class)
.setResultTransformer(Transformers.aliasToBean(type));
return new MyTypedQuery<T>(q);
}
Second, I create a MyTypedQuery, which looks like a wrapper, as below:
public class MyTypedQuery<R> implements TypedQuery<R> {
private Query q;
public MyTypedQuery(Query q) {
this.q = q;
}
@Override
public int executeUpdate() {
return this.q.executeUpdate();
}
@Override
public int getFirstResult() {
return this.q.getFirstResult();
}
@Override
public FlushModeType getFlushMode() {
return this.q.getFlushMode();
}
@Override
public Map<String, Object> getHints() {
return this.q.getHints();
}
@Override
public LockModeType getLockMode() {
return this.q.getLockMode();
}
@Override
public int getMaxResults() {
return this.q.getMaxResults();
}
@Override
public Parameter<?> getParameter(String arg0) {
return this.q.getParameter(arg0);
}
@Override
public Parameter<?> getParameter(int arg0) {
return this.q.getParameter(arg0);
}
@SuppressWarnings("unchecked")
@Override
public <T> Parameter<T> getParameter(String arg0, Class<T> arg1) {
return (Parameter<T>) this.q.getParameter(arg0);
}
@Override
public <T> Parameter<T> getParameter(int arg0, Class<T> arg1) {
return (Parameter<T>) this.q.getParameter(arg0, arg1);
}
@Override
public <T> T getParameterValue(Parameter<T> arg0) {
return (T) this.q.getParameterValue(arg0);
}
@Override
public Object getParameterValue(String arg0) {
return this.q.getParameterValue(arg0);
}
@Override
public Object getParameterValue(int arg0) {
return this.q.getParameterValue(arg0);
}
@Override
public Set<Parameter<?>> getParameters() {
return this.q.getParameters();
}
@Override
public boolean isBound(Parameter<?> arg0) {
return this.q.isBound(arg0);
}
@Override
public <T> T unwrap(Class<T> arg0) {
return this.q.unwrap(arg0);
}
@SuppressWarnings("unchecked")
@Override
public List<R> getResultList() {
return (List<R>) this.q.getResultList();
}
@SuppressWarnings("unchecked")
@Override
public R getSingleResult() {
return (R) this.q.getSingleResult();
}
@Override
public TypedQuery<R> setFirstResult(int arg0) {
this.q.setFirstResult(arg0);
return this;
}
@Override
public TypedQuery<R> setFlushMode(FlushModeType arg0) {
this.q.setFlushMode(arg0);
return this;
}
@Override
public TypedQuery<R> setHint(String arg0, Object arg1) {
this.q.setHint(arg0, arg1);
return this;
}
@Override
public TypedQuery<R> setLockMode(LockModeType arg0) {
this.q.setLockMode(arg0);
return this;
}
@Override
public TypedQuery<R> setMaxResults(int arg0) {
this.q.setMaxResults(arg0);
return this;
}
@Override
public <T> TypedQuery<R> setParameter(Parameter<T> arg0, T arg1) {
this.q.setParameter(arg0, arg1);
return this;
}
@Override
public TypedQuery<R> setParameter(String arg0, Object arg1) {
this.q.setParameter(arg0, arg1);
return this;
}
@Override
public TypedQuery<R> setParameter(int arg0, Object arg1) {
this.q.setParameter(arg0, arg1);
return this;
}
@Override
public TypedQuery<R> setParameter(Parameter<Calendar> arg0, Calendar arg1, TemporalType arg2) {
this.q.setParameter(arg0, arg1, arg2);
return this;
}
@Override
public TypedQuery<R> setParameter(Parameter<Date> arg0, Date arg1, TemporalType arg2) {
this.q.setParameter(arg0, arg1, arg2);
return this;
}
@Override
public TypedQuery<R> setParameter(String arg0, Calendar arg1, TemporalType arg2) {
this.q.setParameter(arg0, arg1, arg2);
return this;
}
@Override
public TypedQuery<R> setParameter(String arg0, Date arg1, TemporalType arg2) {
this.q.setParameter(arg0, arg1, arg2);
return this;
}
@Override
public TypedQuery<R> setParameter(int arg0, Calendar arg1, TemporalType arg2) {
this.q.setParameter(arg0, arg1, arg2);
return this;
}
@Override
public TypedQuery<R> setParameter(int arg0, Date arg1, TemporalType arg2) {
this.q.setParameter(arg0, arg1, arg2);
return this;
}
}
Third (and last), the use is straightforward like:
final List<Car> list =
createNamedAliasQuery("your-named-query", Car.class)
.setParameter("idCar", idCar)
.setParameter("idModel", idModel)
.getResultList();
Note that @SuppressWarnings("unchecked") appears once in our MyTypedQuery and not in every single use.
Solution 11:[11]
First of you have ensure the naming in HQL.
Use the name of the entity in the HQL query. If you don't specify any name in the @Entity annotation then the default is your class name.
For More Information: https://javabydeveloper.com/org-hibernate-hql-internal-ast-querysyntaxexception-entity-table-is-not-mapped/
Solution 12:[12]
I found the best solution here, the key of this issue is the addEntity method
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);
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow