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

LSe*_*rni 80 java generics hibernate

我是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();
    }
}
Run Code Online (Sandbox Code Playgroud)

我想摆脱它SuppressWarnings.但如果我这样做,我会收到警告

Warning: Unchecked cast from List to List<Foo>
Run Code Online (Sandbox Code Playgroud)

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

Warning: List is a raw type. References to generic type List<E>
should be parameterized.
Run Code Online (Sandbox Code Playgroud)

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

它看起来非常像Cast Hibernate结果到一个对象列表,但在这里我没有"硬"错误(系统知道类型Foo,我不是使用SQLQuery而是直接查询).所以没有快乐.

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

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

代码一经编译就没有明显的问题......我知道...... 结果是预期的结果.

那么:我这样做对吗?我错过了一些明显的东西吗 有"官方"或"推荐"的方式吗?

Bor*_*der 95

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

答案很长,Hibernate ListQuery.list方法返回一个raw ,见这里.这不是Hibernate的错误或可以解决的问题,查询返回的类型在编译时是未知的.

因此当你写作

final List<MyObject> list = query.list();
Run Code Online (Sandbox Code Playgroud)

你是从做不安全投ListList<MyObject>-这是无法避免的.

你无法安全地进行施法,因为它List 可以包含任何东西.

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

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

  • 如果hibernate人在`list()`中添加一个类型为`Class <?>`的参数,问题就可以解决了.使用如此丑陋的API是一种耻辱. (7认同)
  • 我喜欢你使用`final`的风格 (5认同)
  • 我只会提出你的答案,希望能有更好的答案.相反,我发现这个问题被Bruce Eckel(Thinking in Java)和Robert Sedgewick(*Sedgewick)称为"丑陋的演员".我还找到了http://stackoverflow.com/questions/509076/how-do-i-address-unchecked-cast-warnings.叹. (4认同)
  • @PeteyPabPro 虽然我同意应该避免使用原始类型,但我不同意将查询结果视为“List&lt;Object&gt;”。结果应转换为预期类型,并应添加 **单元测试** 以确保查询返回正确的结果。在“_later in the code_”中出现查询错误是不可接受的。你的例子是反对在 21 世纪应该被诅咒的编码实践。我建议 ** 绝不** 接受`List&lt;Object&gt;`。 (2认同)

Tau*_*hts 22

解决方案是使用TypedQuery.从EntityManager创建查询时,请调用它:

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

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

  • 附带说明一下,这不适用于您内联创建的纯本机查询。返回的查询只是一个查询,而不是类型查询:( (3认同)

Grz*_*ski 6

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

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

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

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

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

你必须忍受这些警告或抑制它们.

  • >你必须忍受这些警告或压制它们.压制错误的警告总是更好,或者你可以在垃圾邮件中错过正确的警告错误警告 (2认同)

小智 6

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

public class MyDAO {

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

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