94

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?

13 Answers 13

112

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); } 
Sign up to request clarification or add additional context in comments.

16 Comments

I was going to only upvote your answer, hoping a better one would come by. Instead I found out this problem referred to as "ugly cast" by both Bruce Eckel (Thinking in Java) and Robert Sedgewick - the Sedgewick. I also found stackoverflow.com/questions/509076/… . Sigh.
I like your style of using final
If hibernate guys add an argument with type Class<?> in list(), the problem could be solved. It's a shame to use such an ugly API.
@BinWang then the unsafe cast would happen somewhere else, it doesn't solve the problem - it just moves it. Needless to say the HQL API has been effectively deprecated for years now. JPA has a type safe query API called the Criteria Query API.
@PeteyPabPro While I agree that rawtypes should be avoided, I disagree that the results of a query should be treated as a List<Object>. The results should be cast to the expected type and unit tests should be added to ensure that the query returns the right results. It is unacceptable to have errors with queries turn up "later in the code". Your example is an argument against coding practices that should be anathema in the 21st century. It is, I would suggest, never acceptable to have a List<Object>.
|
33

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.

4 Comments

As a side note, this does not work for purely native queries that you are creating inline. The returned query is only ever a query, not a typedquery :(
Now, the error message is gone and the resulting list uses the actual objects instead of the Object object.. great answer!
This appears to be released in hibernate 5.0. I don't see it in 4.3.11 but the following article refers to 5.3. wiki.openbravo.com/wiki/… I also see a Jan 2014 stackoverflow post referring to it: stackoverflow.com/a/21354639/854342.
It is part of hibernate-jpa-2.0-api. You can use this with hibernate 4.3, as I am currently using it on hibernate 4.3.
6

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.

1 Comment

Welcome to Stack Overflow! Anyway, don't forget to take the tour
5

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.

2 Comments

I agree with the pointlessness. I'll suppress, even if it goes against my grain. Thanks all the same, and +1.
>You have to live with these warnings or supress them. It's always better to supress warnings that are wrong, or you can miss right warning in a spam of unsupressed wrong warnings
4

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

4 Comments

What "cast problems"? I have always just casted the list directly, how is the above more concise or safer?
I can't cast directly. Were cast problems because it couldn't make cast from Object to Destination
You know Hibernate can build a Destinstion for you right? Using the select new syntax. This certainly isn't the right approach.
I also had same experience. As my query returns different fields from multiple tables which are not connected with each other. So the only way worked for me was this one. Thanks :)
3
List<Person> list = new ArrayList<Person>(); Criteria criteria = this.getSessionFactory().getCurrentSession().createCriteria(Person.class); for (final Object o : criteria.list()) { list.add((Person) o); } 

1 Comment

Yes, this is basically the same 'ugliness' suggested by Boris, with a cast within the loop.
2

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

3 Comments

I can't test this now, but... what does this change? q is still a Query and therefore q.list() is still a raw java.util.List type. The cast is then still unchecked; having the object type changed internally should avail nothing...
Yes the cast is still unchecked, but with proper naming of your fields, setting a resultTransformer does the job of casting the Objects as your desired POJO. See this stackoverflow post and read this hibernate reference doc on using native queries
from foo where active is not a native query. So there is no need of a result transformer, as the default mapping will be enough. The question is not about casting the POJO fields, but about casting the result object. A result transformer wouldn't help here.
0

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

1 Comment

The question was not about result transformation. It was about casting of Query results - which is still needed in your example. And your example has nothing to do with the original from foo where active.
0

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

Comments

0

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.

Comments

0

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.

4 Comments

what issue is this resolving over using the default typedquery? It seems as though this is just a passthrough to the underlying methods of a typedquery.
Taugenichts it is exactly that. Without that, there were unsafe cast warning through the whole system. Now, in a unique place with a single @SuppressWarnings.
TypedQueries have a type so you won't get a unsafe cast warning. If you just changed this line: final Query q = getSafeEntityManager().createNamedQuery(queryName); to final TypedQuery<Car> q = getSafeEntityManager().createNamedQuery(queryName, Car.class); you could kill your whole wrapper class.
Yes @Taugenichts, you're right. However, in my answer I am talking about "named alias query", i.e., named query that also uses a ResultTransformer (aliasToBean). The direct way you suggested does work perfectly only with named query.
0

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.

enter image description here

For More Information: https://javabydeveloper.com/org-hibernate-hql-internal-ast-querysyntaxexception-entity-table-is-not-mapped/

Comments

0

I use that it work well:

private List<VehicleUserResult> createQueryVehiclesBYUser(Integer userId) { final Session session= getSession(); Query consulta= session.createQuery("from VehicleUser where user=:user"); consulta.setParameter("user",userId); return (List<VehicleUserResult>) consulta.getResultList(); } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.