Как избежать предупреждений о безопасности типов с результатами Hibernate HQL?
Например у меня есть такой запрос:
Query q = sess.createQuery("from Cat cat");
List cats = q.list();
Если я попытаюсь сделать что-то подобное, появится предупреждение "Безопасность типов. Для выражения типа List необходимо выполнить непроверенное преобразование для соответствия List":
List<Cat> cats = q.list();
Есть ли способ избежать этого?
16 ответов
С помощью @SuppressWarnings
Как и предполагалось, везде есть хороший способ сделать это, хотя при каждом вызове нужно вводить текст пальцем. q.list()
,
Есть два других метода, которые я бы предложил:
Написать помощник
Просто рефакторинг всех ваших @SuppressWarnings
в одно место:
List<Cat> cats = MyHibernateUtils.listAndCast(q);
...
public static <T> List<T> listAndCast(Query q) {
@SuppressWarnings("unchecked")
List list = q.list();
return list;
}
Запретить Eclipse генерировать предупреждения для неизбежных проблем
В Eclipse перейдите в "Окно"> "Установки"> "Java"> "Компилятор"> "Ошибки / предупреждения" и в разделе "Общий тип" установите флажок Ignore unavoidable generic type problems due to raw APIs
Это отключит ненужные предупреждения для подобных проблем, подобных описанной выше, которые неизбежны.
Некоторые комментарии:
- Я решил пройти в
Query
вместо результатаq.list()
потому что таким образом этот "читерский" метод может использоваться только для мошенничества в Hibernate, а не для мошенничестваList
в общем. - Вы можете добавить аналогичные методы для
.iterate()
и т.п.
Прошло много времени с тех пор, как вопрос был задан, но я надеюсь, что мой ответ может быть полезен для таких, как я.
Если вы посмотрите на api-документы javax.persistence, то увидите, что с тех пор были добавлены некоторые новые методы. Java Persistence 2.0
, Один из них является createQuery(String, Class<T>)
который возвращается TypedQuery<T>
, Ты можешь использовать TypedQuery
так же, как вы сделали это с Query
с той небольшой разницей, что все операции теперь безопасны для типов.
Итак, просто измените свой код на что-то вроде этого:
Query q = sess.createQuery("from Cat cat", Cat.class);
List<Cat> cats = q.list();
И все готово.
Мы используем @SuppressWarnings("unchecked")
также, но мы чаще всего пытаемся использовать его только для объявления переменной, а не для метода в целом:
public List<Cat> findAll() {
Query q = sess.createQuery("from Cat cat");
@SuppressWarnings("unchecked")
List<Cat> cats = q.list();
return cats;
}
Попробуй использовать TypedQuery
вместо Query
, Например, вместо этого:
Query q = sess.createQuery("from Cat cat", Cat.class);
List<Cat> cats = q.list();
Использовать этот:-
TypedQuery<Cat> q1 = sess.createQuery("from Cat cat", Cat.class);
List<Cat> cats = q1.list();
По-видимому, метод Query.list() в Hibernate API не является безопасным по типу "по замыслу", и его не планируется менять.
Я считаю, что самое простое решение, позволяющее избежать предупреждений компилятора, - это добавить @SuppressWarnings("unchecked"). Эта аннотация может быть размещена на уровне метода или, если внутри метода, прямо перед объявлением переменной.
Если у вас есть метод, который инкапсулирует Query.list() и возвращает List (или Collection), вы также получите предупреждение. Но этот подавляется с помощью @SuppressWarnings("rawtypes").
Метод listAndCast(Query), предложенный Matt Quail, менее гибок, чем Query.list(). Пока я могу сделать:
Query q = sess.createQuery("from Cat cat");
ArrayList cats = q.list();
Если я попробую код ниже:
Query q = sess.createQuery("from Cat cat");
ArrayList<Cat> cats = MyHibernateUtils.listAndCast(q);
Я получу ошибку компиляции: Несоответствие типов: невозможно преобразовать из списка в ArrayList
В нашем коде мы отмечаем вызывающие методы с помощью:
@SuppressWarnings ("флажок")
Я знаю, что это похоже на взлом, но со-разработчик недавно проверил и обнаружил, что это все, что мы могли сделать.
Это не упущение или ошибка. Предупреждение отражает реальную проблему - нет никакого способа, которым java-компилятор действительно может быть уверен, что класс hibernate выполнит свою работу должным образом и что список, который он возвращает, будет содержать только Cats. Любое из предложений здесь хорошо.
Нет, но вы можете выделить его в конкретные методы запроса и подавить предупреждения с помощью @SuppressWarnings("unchecked")
аннотаций.
Новые версии Hibernate теперь поддерживают безопасный тип Query<T>
объект, так что вам больше не придется использовать @SuppressWarnings
или осуществите некоторый взлом, чтобы убрать предупреждения компилятора. В API сеансаSession.createQuery
теперь вернет тип safe Query<T>
объект. Вы можете использовать это так:
Query<Cat> query = session.createQuery("FROM Cat", Cat.class);
List<Cat> cats = query.list();
Вы также можете использовать его, когда результат запроса не вернет Cat:
public Integer count() {
Query<Integer> query = sessionFactory.getCurrentSession().createQuery("SELECT COUNT(id) FROM Cat", Integer.class);
return query.getSingleResult();
}
Или при частичном выборе:
public List<Object[]> String getName() {
Query<Object[]> query = sessionFactory.getCurrentSession().createQuery("SELECT id, name FROM Cat", Object[].class);
return query.list();
}
У нас была такая же проблема. Но для нас это не имело большого значения, потому что нам приходилось решать другие более важные проблемы с Hibernate Query and Session.
В частности:
- контролировать, когда транзакция может быть совершена. (Мы хотели подсчитать, сколько раз tx был "запущен", и фиксировать, только когда tx был "закончен", столько раз, сколько он был запущен. Полезно для кода, который не знает, нужно ли начинать транзакцию. Теперь любой код, который нуждается в передаче, просто "запускает" его и заканчивает, когда все готово.)
- Сбор метрик производительности.
- Задержка запуска транзакции, пока не станет известно, что что-то действительно будет сделано.
- Более мягкое поведение для query.uniqueResult()
Так что для нас мы имеем:
- Создайте интерфейс (AmplafiQuery), который расширяет Query
- Создайте класс (AmplafiQueryImpl), который расширяет AmplafiQuery и оборачивает org.hibernate.Query.
- Создайте Txmanager, который возвращает Tx.
- Tx имеет различные методы createQuery и возвращает AmplafiQueryImpl
И наконец,
AmplafiQuery имеет "asList()", который является универсальной включенной версией Query.list() AmplafiQuery имеет "unique()", которая является универсальной включенной версией Query.uniqueResult() (и просто регистрирует проблему, а не выбрасывает исключение)
Это большая работа, чтобы просто избежать @SuppressWarnings. Тем не менее, как я сказал (и перечислил), есть много других лучше! причины, чтобы сделать упаковочную работу.
Решение Джо Дина выглядит интересно, но считаете ли вы, что оно того стоит - создать новый список и перебрать все элементы, чтобы избавиться от предупреждений?
(извините, по какой-то причине не могу добавить комментарий к своему решению)
Я знаю, что это старше, но на 2 пункта стоит отметить, что сегодня в Matt Quails Answer.
Пункт 1
это
List<Cat> cats = Collections.checkedList(Cat.class, q.list());
Должно быть это
List<Cat> cats = Collections.checkedList(q.list(), Cat.class);
Пункт 2
Из этого
List list = q.list();
к этому
List<T> list = q.list();
уменьшит другие предупреждения, очевидно, в оригинальном ответном теге маркеры были удалены браузером.
Хорошее решение, чтобы избежать предупреждений о безопасности типов с помощью запроса гибернации, - это использовать инструмент, подобный TorpedoQuery, чтобы помочь вам создать безопасный тип hql.
Cat cat = from(Cat.class);
org.torpedoquery.jpa.Query<Entity> select = select(cat);
List<Cat> cats = select.list(entityManager);
Попробуй это:
Query q = sess.createQuery("from Cat cat");
List<?> results = q.list();
for (Object obj : results) {
Cat cat = (Cat) obj;
}
TypedQuery<EntityName> createQuery = entityManager.createQuery("from EntityName", EntityName.class);
List<EntityName> resultList = createQuery.getResultList();
Если вы не хотите использовать @SuppressWarnings("unchecked"), вы можете сделать следующее.
Query q = sess.createQuery("from Cat cat");
List<?> results =(List<?>) q.list();
List<Cat> cats = new ArrayList<Cat>();
for(Object result:results) {
Cat cat = (Cat) result;
cats.add(cat);
}
К вашему сведению - я создал метод util, который делает это для меня, чтобы он не засорял мой код и мне не приходилось использовать @SupressWarning.