Как избежать предупреждений о безопасности типов с результатами 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.

В частности:

  1. контролировать, когда транзакция может быть совершена. (Мы хотели подсчитать, сколько раз tx был "запущен", и фиксировать, только когда tx был "закончен", столько раз, сколько он был запущен. Полезно для кода, который не знает, нужно ли начинать транзакцию. Теперь любой код, который нуждается в передаче, просто "запускает" его и заканчивает, когда все готово.)
  2. Сбор метрик производительности.
  3. Задержка запуска транзакции, пока не станет известно, что что-то действительно будет сделано.
  4. Более мягкое поведение для query.uniqueResult()

Так что для нас мы имеем:

  1. Создайте интерфейс (AmplafiQuery), который расширяет Query
  2. Создайте класс (AmplafiQueryImpl), который расширяет AmplafiQuery и оборачивает org.hibernate.Query.
  3. Создайте Txmanager, который возвращает Tx.
  4. 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.

Другие вопросы по тегам