将Hibernate Query.list()强制转换为List <type>?</type>的“正确”方法是什么?

时间:2013-04-09 21:44:35

标签: java hibernate generics

我是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)不会实现参数化类型,所以我怀疑是我做错了。

它看起来非常像Cast Hibernate result to a list of objects,但在这里我没有“硬”错误(系统知道类型Foo,我不是使用SQLQuery而是直接查询)。所以没有快乐。

我也看了Hibernate Class Cast Exception,因为它看起来很有前景,但后来我意识到我实际上得到任何Exception ...我的问题只是一个警告 - 一种编码风格,如果你愿意的话。

jboss.org上的文档,Hibernate手册和几个教程似乎没有涵盖这样的细节中的主题(或者我没有在正确的位置搜索?)。当他们确实进入细节时,他们会使用即时投射 - 这是在官方jboss.org网站上没有的教程中,所以我有点谨慎。

代码在编译后运行时没有明显的问题......我知道......结果是预期的结果。

所以:我做得对吗?我错过了一些明显的东西吗有没有“官方” 或“推荐”如何做到

11 个答案:

答案 0 :(得分:94)

简短回答@SuppressWarnings是正确的方法。

答案很长,Hibernate从List方法返回原始Query.list,请参阅here。这不是Hibernate的错误或可以解决的问题,查询返回的类型在编译时不知道

因此,当你写

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

您正在进行从ListList<MyObject>的不安全演员表示 - 这是无法避免的。

由于List 可以包含任何内容,因此无法安全地执行广播。

使错误消失的唯一方法是更难看的

final List<MyObject> list = new LinkedList<>();
for(final Object o : query.list()) {
    list.add((MyObject)o);
}

答案 1 :(得分:18)

决议是使用TypedQuery代替。从EntityManager创建查询时,请调用它:

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

对于命名查询,本机命名查询等也是如此。相应的方法与返回vanilla查询的方法具有相同的名称。只要知道返回类型,就可以使用它而不是Query。

答案 2 :(得分:6)

您可以使用类似这样的解决方法来避免编译器警告:

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

但是这段代码存在一些问题:

  • 创建了多余的ArrayList
  • 对查询返回的所有元素进行不必要的循环
  • 更长的代码。

差别只是装饰性的,所以使用这样的解决方法 - 在我看来 - 毫无意义。

你必须忍受这些警告或压制它们。

答案 3 :(得分:3)

要回答你的问题,没有“正确的方法”来做到这一点。 现在,如果这只是困扰你的警告,避免其扩散的最好方法是将Query.list()方法包装到DAO中:

public class MyDAO {

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

这样您只能使用@SuppressWarnings("unchecked")一次。

答案 4 :(得分:2)

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

答案 5 :(得分:2)

您使用类似的ResultTransformer:

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

答案 6 :(得分:1)

对我来说唯一有效的方法是使用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);
    }

使用我发现的其他方法,我遇到了问题

答案 7 :(得分:0)

正确的方法是使用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);

迭代通过Object []是多余的,会有一些性能损失。 有关transofrmers用法的详细信息,请访问: Transformers for HQL and SQL

如果您正在寻找更简单的解决方案,您可以使用开箱即用的地图变换器:

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

答案 8 :(得分:0)

仅使用变形金刚对我来说不起作用,我遇到了类型转换异常。

sqlQuery.setResultTransformer(Transformers.aliasToBean(MYEngityName.class)) 没有用,因为我在返回列表元素中得到了对象数组,而不是列表元素的固定MYEngityName类型。

当我进行以下更改时,它对我有用。当我添加sqlQuery.addScalar(-)每个选定的列及其类型时,对于特定的String类型列,我们不必映射其类型。像addScalar("langCode");

我将MYEngityName与NextEnity结合在一起,我们不能仅在查询中使用select *,它将在返回列表中提供Object数组。

下面的代码示例:

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

这可能会帮助一些人。以这种方式为我工作。

答案 9 :(得分:0)

在一个项目中,我是一名顾问,我遇到了这个问题。看解决办法:
首先,我创建了以下方法:

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

其次,我创建了一个 MyTypedQuery,它看起来像一个包装器,如下所示:

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

第三个(也是最后一个),使用很简单,比如:

final List<Car> list = 
            createNamedAliasQuery("your-named-query", Car.class)
                .setParameter("idCar", idCar)
                .setParameter("idModel", idModel)
                .getResultList();

请注意,@SuppressWarnings("unchecked") 在我们的 MyTypedQuery 中出现一次,而不是在每次使用中出现。

答案 10 :(得分:-1)

我找到了最佳解决方案here,此问题的关键是 addEntity 方法

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