91

我是 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返回 a 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 网站上没有的教程上,所以我有点谨慎。

代码,一旦编译,运行没有明显的问题......我知道......但是;结果是预期的。

所以:我这样做对吗?我错过了一些明显的东西吗?是否有“官方”或“推荐”的方式来做到这一点

4

11 回答 11

107

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

长答案,HibernateList从该Query.list方法返回一个原始值,请参见此处。这不是 Hibernate 的错误或可以解决的问题,查询返回的类型在编译时是未知的。

因此,当你写

final List<MyObject> list = query.list();

您正在从Listto进行不安全的演员阵容List<MyObject>- 这是无法避免的。

您无法安全地进行演员阵容,因为其中List 可能包含任何东西。

使错误消失的唯一方法是更加丑陋

final List<MyObject> list = new LinkedList<>();
for(final Object o : query.list()) {
    list.add((MyObject)o);
}
于 2013-04-09T21:51:03.497 回答
32

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

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

这也适用于命名查询、本机命名查询等。相应的方法与返回原始查询的方法具有相同的名称。只要您知道返回类型,只需使用它而不是查询。

于 2016-03-09T18:28:06.913 回答
6

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

public class MyDAO {

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

这样你就可以@SuppressWarnings("unchecked")只使用一次。

于 2016-04-07T10:56:14.047 回答
5

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

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

但是这段代码存在一些问题:

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

而且区别只是表面上的,所以在我看来,使用这样的解决方法是毫无意义的。

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

于 2013-04-09T22:00:54.417 回答
3

唯一对我有用的方法是使用迭代器。

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

使用我发现的其他方法,我遇到了演员问题

于 2014-04-15T20:05:09.650 回答
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);
}
于 2014-01-11T08:23:35.397 回答
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();
}
于 2015-10-05T23:36:49.567 回答
0

只是使用变形金刚它对我不起作用我得到了类型转换异常。

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

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

而且我已经将 MYEngityName 与 NextEnity 连接起来,我们不能只select *在查询中提供返回列表中的对象数组。

下面的代码示例:

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

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

于 2019-06-20T07:41:36.183 回答
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[] 是多余的,并且会产生一些性能损失。您可以在此处找到有关变压器使用的详细信息: 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");
于 2015-11-21T18:39:21.037 回答
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 中出现一次,而不是在每次使用中。

于 2021-04-13T17:02:56.220 回答
-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);
    }
}
于 2018-02-27T21:07:19.167 回答