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?
List<Person> list = new ArrayList<Person>();
Criteria criteria = this.getSessionFactory().getCurrentSession().createCriteria(Person.class);
for (final Object o : criteria.list()) {
list.add((Person) o);
}
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:
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.
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);
}
}
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");
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.
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.
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.
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();
}
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
Source: Stackoverflow.com