将 Hibernate Query. List()转换为 List < Type > 的“正确”方法是什么?

我是一个使用 Hibernate 的新手,我正在编写一个返回对象列表的简单方法 匹配一个特定的过滤器。 List<Foo>似乎是一个自然的返回类型。

无论我做什么,似乎都不能让编译器满意,除非我使用一个丑陋的 @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();
}
}

我想摆脱那个 SuppressWarnings。但是如果我这样做,我会得到警告

Warning: Unchecked cast from List to List<Foo>

(我可以忽略它,但首先我不想得到它) ,如果我删除泛型以符合 .list()返回类型,我会得到警告

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

我注意到,org.hibernate.mapping 是的声明了一个 List; 但它是一个完全不同的类型-Query返回一个 java.util.List,作为一个原始类型。我觉得最近的 Hibernate (4.0.x)没有实现参数化类型很奇怪,所以我怀疑是我做错了什么。

它看起来非常像 将 Hibernate 结果强制转换为对象列表,但是这里没有“硬”错误(系统知道 Foo 类型,我使用的不是 SQLQuery,而是直接查询)。所以没有乐趣。

我也看了看 Hibernate 类强制转换异常,因为它看起来很有希望,但后来我意识到,我做 没有实际上得到任何 Exception... 我的问题只是一个警告-一种编码风格,如果你愿意。

Documentation on jboss.org, Hibernate manuals and several tutorials do not seem to cover the topic in 这样 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.

代码,一旦编译,运行没有 很明显问题... ... 据我所知... ... 还没有; 结果是预期的。

那么: 我这样做对吗? 我是不是漏掉了什么明显的东西? 有没有一个“正式”的 还是“推荐”就是这样

147335 次浏览

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

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.

List<Person> list = new ArrayList<Person>();
Criteria criteria = this.getSessionFactory().getCurrentSession().createCriteria(Person.class);
for (final Object o : criteria.list()) {
list.add((Person) o);
}

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

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

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

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.

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.

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

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.

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.

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/