我是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网站上没有的教程中,所以我有点谨慎。
代码在编译后运行时没有明显的问题......我知道......结果是预期的结果。
所以:我做得对吗?我错过了一些明显的东西吗有没有“官方” 或“推荐”如何做到?
答案 0 :(得分:94)
简短回答@SuppressWarnings
是正确的方法。
答案很长,Hibernate从List
方法返回原始Query.list
,请参阅here。这不是Hibernate的错误或可以解决的问题,查询返回的类型在编译时不知道。
因此,当你写
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);
}
答案 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);
}
但是这段代码存在一些问题:
差别只是装饰性的,所以使用这样的解决方法 - 在我看来 - 毫无意义。
你必须忍受这些警告或压制它们。
答案 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);
}
}