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


84

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

不管我做什么,除非雇用了ugly,否则似乎都无法使编译器满意@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返回ajava.util.List作为原始类型。我发现最近的Hibernate(4.0.x)无法实现参数化类型很奇怪,因此我怀疑是我自己在做错事情。

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

我也看过Hibernate Class Cast Exception,因为它看起来很有希望,但是后来我意识到我实际上并没有得到任何信息Exception……我的问题只是一个警告的问题-如果可以的话,是一种编码样式。

关于jboss.org的文档,Hibernate手册和一些教程似乎没有如此详细地涵盖该主题(或者我没有在正确的位置搜索?)。当他们确实进入细节时,他们会使用即时转换-以及不在jboss.org官方网站上的教程中使用的转换,因此我有些警惕。

代码一旦编译,就不会出现明显的问题……我知道……。结果是预期的。

所以:我这样做对吗?我是否缺少明显的东西?有“正式”或“推荐”的方式吗?

Answers:


101

简短的答案@SuppressWarnings是正确的方法。

长答案,HibernateList从该Query.list方法返回一个原始值,请参见此处。这不是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);
}

4
我只是赞成你的答案,希望能有一个更好的答案。相反,我发现了Bruce Eckel(使用Java进行思考)和Robert Sedgewick(Sedgewick )都将其称为“丑陋的转换”的问题。我还找到了stackoverflow.com/questions/509076/…。叹。
LSerni

6
我喜欢您的使用方式final
Pavel

9
如果休眠的人添加一个Class<?>in类型的参数list(),则可以解决该问题。使用如此丑陋的API很可惜。
Bin Bin

@BinWang然后不安全的转换将发生在其他地方,它不能解决问题-只是将其移动。毋庸置疑,HQL API已被有效弃用多年。JPA有一个类型安全的查询API,称为“条件查询API”
蜘蛛鲍里斯(Boris the Spider)

2
@PeteyPabPro虽然我同意应避免使用原始类型,但我不同意将查询结果视为List<Object>。结果应转换为预期的类型,并应添加单元测试以确保查询返回正确的结果。在“代码稍后”出现查询错误是不可接受的。您的示例提出了反对编码实践的论点,该实践在21世纪将是一场恶战。我建议,绝对不能接受List<Object>
蜘蛛鲍里斯(Boris)

26

解决方法是改用TypedQuery。当从EntityManager创建查询时,应这样调用它:

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

这对于命名查询,本机命名查询等也相同。相应的方法具有与将返回原始查询的名称相同的名称。只要知道返回类型,就使用它代替查询。


1
附带说明,这不适用于您直接创建内联的纯本地查询。返回的查询仅是查询,而不是typedquery :(
Taugenichts

现在,错误消息消失了,结果列表使用了实际的对象而不是Object对象。
约翰内斯

它似乎在休眠5.0中发布。我在4.3.11中看不到它,但是以下文章引用了5.3。wiki.openbravo.com/wiki/…我还看到2014年1月的stackoverflow帖子引用了它:stackoverflow.com/a/21354639/854342
柯蒂斯·雅洛普

它是hibernate-jpa-2.0-api的一部分。您可以将其与hibernate 4.3一起使用,因为我目前在hibernate 4.3上使用它。
Taugenichts

6

您可以使用以下解决方法来避免编译器警告:

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

但是此代码存在一些问题:

  • 创建多余的ArrayList
  • 查询返回的所有元素上不必要的循环
  • 更长的代码。

而且区别只是外观上的,因此,我认为使用这种解决方法是没有意义的。

您必须忍受这些警告或压制它们。


1
我同意毫无意义。我会压制,即使这违背我的意愿。都一样,谢谢,+ 1。
LSerni

2
>您必须忍受这些警告或限制它们。最好禁止发出错误的警告,否则,您可能会在未受到抑制的错误警告的垃圾邮件中错过正确的警告
SpongeBobFan

6

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

public class MyDAO {

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

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


欢迎使用Stack Overflow!无论如何,不要忘记带
Sнаđошƒаӽ

3

对我而言唯一可行的方法是使用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);
    }

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


什么是“铸造问题”?我一直只是直接将列表投下,上面的内容如何更简洁或更安全?
Giovanni Botta 2014年

我不能直接投。由于无法将对象从对象投射到目标而被投放了问题
Popa Andrei 2014年

您知道Hibernate可以Destinstion为您建立一个对吗?使用select new语法。这当然不是正确的方法。
蜘蛛鲍里斯(Boris the Spider)

我也有同样的经历。当我的查询从多个彼此不连接的表中返回不同的字段时。所以对我有用的唯一方法就是这种方法。谢谢:)
Chintan Patel 2015年

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

是的,这基本上与鲍里斯(Boris)建议的“丑陋”相同,并且在循环内进行了强制转换。
LSerni 2014年

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

1
我现在无法测试,但是...这有什么变化?q仍然是Query,因此q.list()仍然是原始java.util.List类型。然后仍然不检查演员表;在内部更改对象类型应该没有任何用处……
Lserni 2015年

是的,仍然没有选中强制类型转换,但是通过正确命名字段,设置resultTransformer可以将对象强制转换为所需的POJO。请参阅此stackoverflow帖子,并阅读有关使用本机查询的休眠参考文档
lakreqta 2015年

from foo where active不是本机查询。因此,不需要结果转换器,因为默认映射就足够了。问题不在于强制转换POJO字段,而在于强制转换结果对象。结果转换器在这里无济于事。
Tobias Liefke

0

正确的方法是使用休眠变压器:

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 []是多余的,并且会降低性能。有关转炉使用的详细信息,您将在这里找到: 用于HQL和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");

问题不在于结果转换。这是关于Query结果的转换-在您的示例中仍然需要。您的示例与原始示例无关from foo where active
Tobias Liefke

0

仅使用Transformers对我不起作用,我遇到了类型转换异常。

sqlQuery.setResultTransformer(Transformers.aliasToBean(MYEngityName.class)) 没有用,因为我在返回列表元素中获取了对象数组,而不是列表元素的固定MYEngityName类型。

当我进行以下更改时sqlQuery.addScalar(-),它对我有用。当我添加了每个选定的列及其类型时,对于特定的String类型列,我们不必映射其类型。喜欢addScalar("langCode");

我将MYEngityName与NextEnity结合在一起,我们不能仅仅select *在Query中将它返回列表中的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();

它可能会有所帮助。以这种方式为我工作。


-1

我在这里找到了最佳解决方案,此问题的关键是 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);
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.