Я новичок в 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 для списка объектов , но здесь у меня нет "серьезных" ошибок (система знает тип Foo, и я использую не SQLQuery, а простой запрос). Так что никакой радости.
Я также посмотрел на исключение Hibernate Class Cast Exception, так как оно выглядело многообещающим, но затем я понял, что на самом деле ничего не получаю Exception
... моя проблема - это просто предупреждение - стиль кодирования, если хотите.
Документация на jboss.org, руководства по Hibernate и несколько руководств, похоже, не раскрывают эту тему так подробно (или я не искал в нужных местах?). Когда они вводят детали, они используют кастинг на лету - и это в обучающих программах, которых не было на официальном сайте jboss.org, поэтому я немного насторожен.
После компиляции код работает без явных проблем ... о которых я знаю ... пока; и результаты ожидаемые.
Итак: правильно ли я делаю? Я упускаю что-то очевидное? Есть ли «официальный» или «рекомендуемый» способ сделать это ?
final
Class<?>
inlist()
, проблема может быть решена. Обидно использовать такой уродливый API.List<Object>
. Результаты должны быть приведены к ожидаемому типу и должны быть добавлены модульные тесты , чтобы гарантировать, что запрос возвращает правильные результаты. Недопустимо, чтобы ошибки с запросами появлялись « позже в коде ». Ваш пример - аргумент против практики кодирования, которая должна быть анафемой в 21 веке. Я бы сказал, что никогда не приемлемо иметьList<Object>
.Решение - использовать вместо этого TypedQuery. При создании запроса из EntityManager вместо этого назовите его так:
TypedQuery<[YourClass]> query = entityManager.createQuery("[your sql]", [YourClass].class); List<[YourClass]> list = query.getResultList(); //no type warning
Это также работает так же для именованных запросов, собственных именованных запросов и т. Д. Соответствующие методы имеют те же имена, что и те, которые возвращают стандартный запрос. Просто используйте это вместо запроса, когда вы знаете тип возвращаемого значения.
источник
Вы можете избежать предупреждения компилятора с помощью обходных приемов, подобных этому:
List<?> resultRaw = query.list(); List<MyObj> result = new ArrayList<MyObj>(resultRaw.size()); for (Object o : resultRaw) { result.add((MyObj) o); }
Но с этим кодом есть некоторые проблемы:
И разница носит чисто косметический характер, поэтому использовать такие обходные пути, на мой взгляд, бессмысленно.
Вы должны жить с этими предупреждениями или подавлять их.
источник
Чтобы ответить на ваш вопрос, не существует "правильного способа" сделать это. Теперь, если вас беспокоит просто предупреждение, лучший способ избежать его распространения - обернуть
Query.list()
метод в DAO:public class MyDAO { @SuppressWarnings("unchecked") public static <T> List<T> list(Query q){ return q.list(); } }
Таким образом, вы сможете использовать
@SuppressWarnings("unchecked")
только один раз.источник
Единственный способ, которым я работал, был с Итератором.
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); }
С другими методами, которые я нашел, у меня были проблемы
источник
Destinstion
для вас? Используяselect new
синтаксис. Это определенно неправильный подход.List<Person> list = new ArrayList<Person>(); Criteria criteria = this.getSessionFactory().getCurrentSession().createCriteria(Person.class); for (final Object o : criteria.list()) { list.add((Person) o); }
источник
Вы используете 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(); }
источник
q
все еще являетсяQuery
и, следовательноq.list()
, остается сырымjava.util.List
типом. Приведение в этом случае все еще не отмечено; изменение типа объекта изнутри ничего неfrom foo where active
это не родной запрос. Таким образом, нет необходимости в преобразователе результатов, так как сопоставления по умолчанию будет достаточно. Вопрос не в приведении полей POJO, а в приведении объекта результата. Преобразователь результатов здесь не поможет.Правильный способ - использовать 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");
источник
Query
результатов - что все еще необходимо в вашем примере. И ваш пример не имеет ничего общего с оригиналомfrom foo where active
.Просто используя трансформаторы. У меня не получилось, я получал исключение приведения типа.
sqlQuery.setResultTransformer(Transformers.aliasToBean(MYEngityName.class))
не сработало, потому что я получал массив объектов в элементе списка возврата, а не фиксированный тип MYEngityName элемента списка.У меня это сработало, когда я внес следующие изменения. Когда я добавил
sqlQuery.addScalar(-)
каждый выбранный столбец и его тип, а для определенного столбца типа String нам не нужно отображать его тип. нравиться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();
Это может кому-нибудь помочь. таким образом работает для меня.
источник
Я нашел лучшее решение здесь , ключ этого вопроса является 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); } }
источник