将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();
你正在做List
到List<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");