Подтвердить что ты не робот

Каков "правильный" способ перевести Hibernate Query.list() в List <Type>?

Я новичок с 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) не будет реализовывать параметризованные типы, поэтому я подозреваю, что он вместо этого сделал что-то неправильно.

Он очень похож на результат слияния гибернации на список объектов, но здесь у меня нет "жестких" ошибок (система знает тип Foo и I) m не используя SQLQuery, а прямой запрос). Так что никакой радости.

Я также рассмотрел Hibernate Class Cast Exception, поскольку он выглядел многообещающим, но потом я понял, что на самом деле я не получаю никаких Exception... моей проблемы это только предупреждение - стиль кодирования, если вы это сделаете.

Документация на jboss.org, руководства Hibernate и несколько руководств, похоже, не охватывают тему такими подробностями (или я не искал в нужных местах?). Когда они вступают в детали, они используют литье на лету - и это на уроках, которые не были на официальном сайте jboss.org, поэтому я немного насторожен.

Код, однажды скомпилированный, работает без видимых проблем... что я знаю... еще; и результаты ожидаются.

Итак: правильно ли я это делаю? Мне что-то не хватает? Есть ли "официальная" или "рекомендуемый" способ сделать это?

4b9b3361

Ответ 1

Короткий ответ @SuppressWarnings - правильный путь.

Длинный ответ, Hibernate возвращает raw List из метода 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);
}

Ответ 2

Решением является использование TypedQuery. При создании запроса из EntityManager вместо этого вызывайте его следующим образом:

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

Это также работает для именованных запросов, собственных именованных запросов и т.д. Соответствующие методы имеют те же имена, что и те, которые возвращают ванильный запрос. Просто используйте это вместо запроса, когда вы знаете тип возвращаемого значения.

Ответ 3

Вы можете избежать предупреждения компилятора с обходными методами, подобными этому:

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

Но есть некоторые проблемы с этим кодом:

  • создал лишний ArrayList
  • ненужный цикл для всех элементов, возвращаемых из запроса
  • более длинный код.

И разница только косметическая, поэтому использование таких обходных решений - на мой взгляд - бессмысленно.

Вы должны жить с этими предупреждениями или подавлять их.

Ответ 4

Чтобы ответить на ваш вопрос, для этого нет "правильного пути". Теперь, если это только предупреждение, которое вас беспокоит, лучший способ избежать его распространения - обернуть метод Query.list() в DAO:

public class MyDAO {

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

Таким образом вы можете использовать @SuppressWarnings("unchecked") только один раз.

Ответ 5

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

Ответ 6

Вы используете 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();
}

Ответ 7

Только для меня это работает с Итератором.

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

С другими методами, которые я нашел, у меня были проблемы с броском

Ответ 8

Правильный способ - использовать 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);

Итерирование сквозного объекта [] является избыточным и имеет некоторое ограничение производительности. Подробную информацию об использовании трансформеров вы найдете здесь: Трансформаторы для 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");

Ответ 9

Просто использование Трансформеров. Это не сработало, я получал исключение приведения типов.

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

Это может помочь кому-то. таким образом, работа для меня.

Ответ 10

Я нашел лучшее решение здесь, ключом к этому вопросу является метод 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);
    }
}