What is the “right” way to translate Hibernate Query.list () to List <Type>?

I am new to Hibernate, and I am writing a simple method to return a list of objects matching a specific filter. List<Foo> seemed like a natural return type.

Whatever I do, I can't make the compiler happy unless I use the 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 this SuppressWarnings . But if I do, I get a warning

 Warning: Unchecked cast from List to List<Foo> 

(I can ignore it, but I would like not to get it), and if I delete the generic to match the return type .list() , I get a warning

 Warning: List is a raw type. References to generic type List<E> should be parameterized. 

I noticed that org.hibernate.mapping declares a List ; but it is a completely different type - Query returns java.util.List as a raw type. It seemed strange to me that the recent Hibernate (4.0.x) would not implement parameterized types, so I suspect that it did something wrong instead.

It is very similar to the result of merging hibernation into a list of objects , but here I have no "hard" errors (the system knows the type of Foo and I) m not using SQLQuery, but a direct query). So no joy.

I also looked at the Hibernate Class Cast Exception , because it looked promising, but then I realized that I really don't get any Exception ... my problem is just a warning - the coding style if you do.

The documentation on jboss.org, the Hibernate guides, and a few guides do not seem to cover the topic in such detail (or have I not searched in the right places?). When they enter into the details, they use casting on the fly - and this is in lessons that were not on the official jboss.org website, so I'm a little wary.

The code, once compiled, works without visible problems ... what I know ... more; and results are expected.

So: am I doing this right? Am I missing something? Is there an “official” or “recommended” way to do this?

+78
java generics hibernate
Apr 09 '13 at 21:44
source share
10 answers

@SuppressWarnings short answer is the right way.

Long answer, Hibernate returns a raw List from the Query.list method, see here . This is not a bug with Hibernate or something that can be solved, the type returned by the request is unknown at compile time.

Therefore when you write

 final List<MyObject> list = query.list(); 

You are performing an unsafe listing from List to List<MyObject> - this cannot be avoided.

It is not possible to safely perform a cast because List may contain anything.

The only way to avoid a mistake is even more ugly

 final List<MyObject> list = new LinkedList<>(); for(final Object o : query.list()) { list.add((MyObject)o); } 
+94
Apr 09 '13 at 21:51
source share

The solution is to use TypedQuery. When creating a query from EntityManager, call it instead as follows:

 TypedQuery<[YourClass]> query = entityManager.createQuery("[your sql]", [YourClass].class); List<[YourClass]> list = query.getResultList(); //no type warning 

This also works for named queries, native named queries, etc. The corresponding methods have the same names as those that return the vanilla request. Just use this instead of querying when you know the type of return value.

+19
Mar 09 '16 at 18:28
source share

You can avoid the compiler warning with workarounds like this:

 List<?> resultRaw = query.list(); List<MyObj> result = new ArrayList<MyObj>(resultRaw.size()); for (Object o : resultRaw) { result.add((MyObj) o); } 

But there are some problems with this code:

  • created an extra ArrayList
  • unnecessary loop for all items returned from the request
  • longer code.

And the difference is only cosmetic, so the use of such workarounds is, in my opinion, pointless.

You must live with these suppressions or suppress them.

+6
Apr 09 '13 at 22:00
source share

There is no “right way” to answer your question. Now, if this is just a warning that bothers you, the best way to avoid spreading it is to wrap the Query.list() method in a DAO:

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

This way you can use @SuppressWarnings("unchecked") only once.

+3
Apr 7 '16 at 10:56 on
source share
 List<Person> list = new ArrayList<Person>(); Criteria criteria = this.getSessionFactory().getCurrentSession().createCriteria(Person.class); for (final Object o : criteria.list()) { list.add((Person) o); } 
+2
Jan 11 '14 at 8:23
source share

You are using ResultTransformer as follows:

 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(); } 
+2
05 Oct '15 at 23:36
source share

Only for me it works 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

+1
Apr 15 '14 at 20:05
source share

The correct 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 through the object [] is redundant and has some performance limitation. You will find detailed information on using transformers here: Transformers for HQL and SQL

If you are looking for an even simpler solution, you can use the built-in card 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"); 
0
Nov 21 '15 at 18:39
source share

Just using Transformers. This did not work; I was getting a cast exception.

sqlQuery.setResultTransformer(Transformers.aliasToBean(MYEngityName.class)) did not work because I was getting an array of objects in the return list item, and not the fixed type of the list item MYEngityName.

This worked for me when I made the following changes. When I added sqlQuery.addScalar(-) each selected column and its type, and for a column of a specific type of String, we do not need to display its type. like addScalar("langCode");

And I joined MYEngityName with NextEnity, which we cannot just select * in the request, it will give an Object array in the return list.

Below is a sample code:

 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 someone. thus, work for me.

0
Jun 20 '19 at 7:41
source share

I found a better solution here , the key to this question 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); } } 
-one
Feb 27 '18 at 21:07
source share



All Articles