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

我是Hibernate的新手,我正在写一个简单的方法来返回匹配特定filter的对象列表。 List<Foo>似乎是一个自然的返回types。

无论我做什么,除非我使用丑陋的@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()返回types,我得到警告

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

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

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

我也看过Hibernate的类转换exception,因为它看起来很有希望,但后来我意识到,我实际上没有得到任何Exception …我的问题只是一个警告 – 编码风格,如果你愿意。

关于jboss.org的文档,Hibernate手册和几个教程似乎没有详细讨论这个话题(或者我没有在正确的地方search)。 当他们详细介绍的时候,他们会使用即时演员 – 而这个教程不在官方的jboss.org网站上,所以我有点警惕。

这个代码一旦编译完成,就没有明显的问题…我知道… 结果是预期的结果。

那么,我是这样做的吗? 我错过了什么明显的? 有没有“官方”或“推荐”的方法

简短的回答@SuppressWarnings是正确的路要走。

长的答案,Hibernate从Query.list方法返回一个原始List ,见这里 。 这不是Hibernate的一个错误或者可以解决的问题,查询返回的types在编译时是不知道的

因此,当你写

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

解决方法是使用TypedQuery来代替。 从EntityManager创build查询时,请调用它像这样:

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

这也适用于命名查询,本地命名查询等。相应的方法与返回vanilla查询的名称相同。 只要知道返回types,就用这个来代替Query。

你可以避免像这样的解决方法的编译器警告:

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

但是这个代码有一些问题:

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

差别只是表面的,所以使用这种解决方法在我看来是毫无意义的。

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

尝试这个:

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

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

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

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

你使用这样的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(); } 

只有这样才能为我工作的是一个迭代器。

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

用我发现的其他方法,我投了问题

正确的方法是使用hibernate变形金刚:

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

迭代通过对象[]是多余的,会有一些性能损失。 有关变压器使用情况的详细信息,您可以在这里find: HQL和SQL变压器

如果你正在寻找更简单的解决scheme,你可以使用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");