如何避免使用Hibernate HQL结果时出现类型安全警告?

110
例如,我有以下查询:

For example I have such query:


Query q = sess.createQuery("from Cat cat");
List cats = q.list();

如果我尝试制作类似这样的东西,它会显示以下警告:

Type safety: The expression of type List needs unchecked conversion to conform to List<Cat>


List<Cat> cats = q.list();

有没有避免它的方法?


11
值得一提的是,使用JPA时,您可以通过将类型添加到createQuery中来实现类型安全查询,而不会改变原有含义。 - Elazar Leibovich
7
稍有些晚了,但是像Elazar所提到的那样,代码应为sess.createQuery("from Cat cat", Cat.class); - Dominik Mohr
15个回答

101

按照建议,在每个地方都使用 @SuppressWarnings 是一种很好的做法,尽管每次调用 q.list() 时需要打一些字。

我建议另外两种技术:

编写转换辅助工具

将所有的 @SuppressWarnings 简单地重构到一个地方:

List<Cat> cats = MyHibernateUtils.listAndCast(q);

...

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

防止Eclipse为不可避免的问题产生警告

在Eclipse中,进入“Window(窗口)”>“Preferences(首选项)”>“Java”>“Compiler”>“Errors/Warnings(错误/警告)”,在“Generic type(泛型类型)”下,选择复选框Ignore unavoidable generic type problems due to raw APIs(忽略由于原始API引起的无法避免的泛型类型问题)

这将关闭类似上述不可避免问题的不必要警告。

一些评论:

  • 我选择传递Query而不是q.list()的结果,因为这种“作弊”方法只能用于欺骗Hibernate,而不能欺骗任何一般的List
  • 您可以为.iterate()等添加类似的方法。

20
乍一看,Collections.checkedList(Collection<E>,Class<E>)方法似乎是完美的解决方案。然而,Java文档表示,它只能通过该方法生成的类型安全视图来防止添加错误类型的元素,对于给定的列表不做任何检查。 - phatblat
11
在Eclipse中,“List<Cat> list = Collections.checkedList(q.list(), Cat.class);”仍然需要“@SuppressWarnings”。至于另一个提示:“listAndCast”这个单词并不比自动添加的“@SuppressWarnings”更短。 - Tristan
2
顺便提一下,Collections.checkedList() 方法不会抑制未检查的赋值警告。 - Diablo

46

虽然这个问题被问了很长时间,但我希望我的答案对像我一样的某些人有所帮助。

如果您查看 javax.persistence api 文档,您将看到自从 Java Persistence 2.0 以来已经添加了一些新方法。其中之一是 createQuery(String, Class<T>),它返回 TypedQuery<T>。您可以像使用 Query 一样使用 TypedQuery,只是现在所有操作都是类型安全的。

因此,只需将您的代码更改为以下内容:

Query q = sess.createQuery("from Cat cat", Cat.class);
List<Cat> cats = q.list();

一切已经准备就绪。


1
问题与JPA无关。 - Mathijs Segers
4
Hibernate的最近版本实现了JPA 2.x,因此这个答案是相关的。 - caspinos
TypedQuery<T> 是最佳方案。 - Muneeb Mirza
我遇到了这个错误:接口org.hibernate.SharedSessionContract中的方法createQuery无法应用于给定类型; [ERROR] 需要: java.lang.String [ERROR] 找到: java.lang.String,java.lang.Class<MyObjectClassName>。看起来Java不喜欢我的第二个参数。 - mercury

21

我们也会使用@SuppressWarnings("unchecked"),但通常仅尝试在变量声明上使用它,而不是整个方法:

public List<Cat> findAll() {
    Query q = sess.createQuery("from Cat cat");
    @SuppressWarnings("unchecked")
    List<Cat> cats = q.list();
    return cats;
}

17

尝试使用TypedQuery而不是Query。例如,不要像这样使用:

Query q = sess.createQuery("from Cat cat", Cat.class);
List<Cat> cats = q.list();

请使用:

TypedQuery<Cat> q1 = sess.createQuery("from Cat cat", Cat.class);
List<Cat> cats = q1.list();

1
有没有办法用“Criteria”来实现这个? - Stealth Rabbi

5
显然,Hibernate API中的Query.list()方法不是类型安全的“设计”,并且没有计划更改它。
我认为避免编译器警告的最简单的解决方案是添加@SuppressWarnings("unchecked")。此注释可以放置在方法级别或者如果在方法内部,则紧挨着变量声明前面。
如果您有一个封装了Query.list()并返回List(或Collection)的方法,您也会收到警告。但是这个警告可以使用@SuppressWarnings("rawtypes")来抑制。
Matt Quail提出的listAndCast(Query)方法比Query.list()方法不够灵活。虽然我可以执行:
Query q = sess.createQuery("from Cat cat");
ArrayList cats = q.list();

如果我尝试下面的代码:

Query q = sess.createQuery("from Cat cat");
ArrayList<Cat> cats = MyHibernateUtils.listAndCast(q);

我会收到一个编译错误:类型不匹配:无法将List转换为ArrayList


1
“目前没有计划更改。”- 这是2005年的帖子。如果从那时起事情没有改变,我会感到惊讶。 - Rup

5
在我们的代码中,我们使用以下注释来标记调用方法:
@SuppressWarnings("unchecked")
我知道这似乎是一个hack,但是另一位开发人员最近检查过,并发现这是我们所能做的全部。

4
这不是疏忽或错误。警告反映出一个真正的根本问题 - Java编译器无法确保Hibernate类能够正确地完成其工作,并且返回的列表只包含Cats。这里提供的任何建议都是可以接受的。

2
Hibernate的新版本现在支持类型安全的Query<T>对象,因此您不再需要使用@SuppressWarnings或实现某些技巧来消除编译器警告。在Session API中,Session.createQuery现在将返回一个类型安全的Query<T>对象。您可以这样使用它:
Query<Cat> query = session.createQuery("FROM Cat", Cat.class);
List<Cat> cats = query.list();

当查询结果不会返回Cat时,您也可以使用它:

public Integer count() {
    Query<Integer> query = sessionFactory.getCurrentSession().createQuery("SELECT COUNT(id) FROM Cat", Integer.class);
    return query.getSingleResult();
}

或者在执行部分选择时:

public List<Object[]> String getName() {
    Query<Object[]> query = sessionFactory.getCurrentSession().createQuery("SELECT id, name FROM Cat", Object[].class);
    return query.list();
}

2
不可以,但你可以将其隔离到特定的查询方法中,并使用@SuppressWarnings("unchecked")注释来抑制警告。

错误...乔迪恩是正确的,你可以使用?作为通用类型来避免警告... - Mike Stone
1
这不是真的。如果您使用List <?>,则不能将列表的元素用作Cat,而无需创建重复列表并转换每个项目。 - Dave L.
如果您直接使用转换后的结果,则无需创建列表。而且,问题是“是否有方法可以避免它”,答案绝对是肯定的(即使没有抑制警告)。 - Mike Stone

1

我们曾经遇到过同样的问题。但对我们来说并不是什么大问题,因为我们需要解决其他更重要的Hibernate Query和Session问题。

具体而言:

  1. 控制何时可以提交事务。(我们想要计算一个事务被“启动”了多少次,并且只有在该事务被“结束”与启动次数相同时才提交。这对于不知道是否需要启动事务的代码非常有用。现在,任何需要事务的代码都只需“启动”一个事务,并在完成后结束它。)
  2. 性能指标收集。
  3. 延迟启动事务,直到确切知道将要执行某些操作。
  4. 更温和的行为查询.uniqueResult()

所以对于我们来说,我们有:

  1. 创建一个接口(AmplafiQuery),它扩展了Query
  2. 创建一个类(AmplafiQueryImpl),它扩展了AmplafiQuery并包装了org.hibernate.Query
  3. 创建一个Txmanager,返回一个Tx。
  4. Tx具有各种createQuery方法,并返回AmplafiQueryImpl

最后,

AmplafiQuery具有“asList()”,它是Query.list()的通用版本。 AmplafiQuery具有“unique()”,它是Query.uniqueResult()的通用版本(仅记录问题而不是引发异常)。
这样做只是为了避免@SuppressWarnings,需要付出很多努力。但是,正如我所说(并列出的),还有许多其他更好的原因要进行包装工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接