Каков "правильный" способ перевести 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, поэтому я немного насторожен.
Код, однажды скомпилированный, работает без видимых проблем... что я знаю... еще; и результаты ожидаются.
Итак: правильно ли я это делаю? Мне что-то не хватает? Есть ли "официальная"
или "рекомендуемый" способ сделать это?
Ответы
Ответ 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);
}
}