Как избежать предупреждений о безопасности типов с результатами Hibernate HQL?

105

Например, у меня есть такой запрос:

Query q = sess.createQuery("from Cat cat");
List cats = q.list();

Если я попытаюсь сделать что-то вроде этого, появится следующее предупреждение

Type safety: The expression of type List needs unchecked conversion to conform to List<Cat>


List<Cat> cats = q.list();

Есть ли способ избежать этого?

серг
источник
11
Стоит упомянуть, что с JPA вы можете создавать безопасные по типу запросы, добавляя тип в createQuery.
Элазар Лейбович
5
Немного поздно, но sess.createQuery("from Cat cat", Cat.class);как упомянул Элазар.
Доминик Мор

Ответы:

99

Использование @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 перейдите в Window> Preferences> Java> Compiler> Errors / Warnings и в разделе Generic type установите флажок Ignore unavoidable generic type problems due to raw APIs

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

Некоторые комментарии:

  • Я решил передать Queryвместо результата, q.list()потому что таким образом этот метод "обмана" можно использовать только для обмана с Hibernate, а не для обмана Listвообще.
  • Вы можете добавить аналогичные методы для .iterate()т. Д.
Мэтт Куэйл
источник
20
На первый взгляд, метод Collections.checkedList (Collection <E>, Class <E>) выглядит идеальным решением. Однако в javadoc говорится, что он только предотвращает добавление неправильно типизированных элементов через безопасное представление, которое генерирует метод. В данном списке проверка не выполняется.
phatblat 07
11
"Список <Cat> list = Collections.checkedList (q.list (), Cat.class);" по-прежнему требуется "@SuppressWarnings" в Eclipse. О другом совете: ввод «listAndCast» на самом деле не короче, чем «@SuppressWarnings», который автоматически добавляется через Eclipse.
Тристан
2
Кстати, Collections.checkedList()метод не подавляет предупреждение о непроверенном назначении.
Diablo
39

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

Если вы посмотрите документацию javax.persistence api , вы увидите, что с тех пор туда были добавлены некоторые новые методы 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();

И все готово.

Antonpp
источник
1
Вопрос не в JPA
Mathijs Segers
2
Последние версии Hibernate реализуют JPA 2.x, поэтому этот ответ актуален.
caspinos
TypedQuery <T> - лучший сценарий.
Muneeb Mirza
21

Мы @SuppressWarnings("unchecked")тоже используем, но чаще всего пытаемся использовать его только для объявления переменной, а не для метода в целом:

public List<Cat> findAll() {
    Query q = sess.createQuery("from Cat cat");
    @SuppressWarnings("unchecked")
    List<Cat> cats = q.list();
    return cats;
}
кретцель
источник
15

Попробуйте использовать 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();
Шивам Оберой
источник
1
Есть ли способ сделать это Criteria?
Stealth Rabbi
5

В нашем коде мы аннотируем вызывающие методы:

@SuppressWarnings ("не отмечено")

Я знаю, что это похоже на взлом, но один из разработчиков недавно проверил и обнаружил, что это все, что мы могли сделать.

тишок
источник
5

Очевидно, что метод Query.list () в Hibernate API не является типобезопасным «по замыслу», и нет никаких планов его менять .

Я считаю, что самым простым решением, позволяющим избежать предупреждений компилятора, действительно является добавление @SuppressWarnings («не отмечено»). Эта аннотация может быть размещена на уровне метода или, если внутри метода, прямо перед объявлением переменной.

Если у вас есть метод, который инкапсулирует Query.list () и возвращает List (или Collection), вы также получаете предупреждение. Но этот подавляется с помощью @SuppressWarnings ("rawtypes").

Метод listAndCast (Query), предложенный Мэттом Куэллом, менее гибкий, чем 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

Пауло Мерсон
источник
1
«нет никаких планов менять это». - это сообщение из 2005 года. Я был бы удивлен, если бы с тех пор ничего не изменилось.
Rup
4

Это не оплошность или ошибка. Предупреждение отражает реальную основную проблему - компилятор java никак не может быть уверен, что класс hibernate будет правильно выполнять свою работу и что возвращаемый им список будет содержать только кошек. Любое из предложений здесь в порядке.

Полмуррей
источник
2

Нет, но вы можете выделить его в определенные методы запроса и подавить предупреждения с помощью @SuppressWarnings("unchecked")аннотации.

Дэйв Л.
источник
Неправильно ... Джо Дин прав, вы можете использовать? как общий тип, чтобы избежать предупреждений ...
Майк Стоун,
1
Это не правда. Если вы используете List <?>, Вы не можете использовать элементы списка как Cat без ненужного шага по созданию дублирующего списка и приведению каждого элемента.
Дэйв Л.
ну, если вы используете результаты напрямую через кастинг, вам не нужно создавать список, и, несмотря на это, вопрос был «есть ли способ избежать этого», ответ определенно ДА (даже без предупреждений о подавлении)
Майк Stone
2

Новые версии Hibernate теперь поддерживают типобезопасный Query<T>объект, поэтому вам больше не нужно использовать @SuppressWarningsили внедрять какие-либо хаки, чтобы убрать предупреждения компилятора. В API Session , Session.createQueryтеперь будет возвращать тип безопасный 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();
}
Дэвид ДеМар
источник
1

У нас была такая же проблема. Но для нас это не имело большого значения, потому что нам нужно было решить другие более серьезные проблемы с помощью Hibernate Query и Session.

В частности:

  1. контролировать, когда транзакция может быть совершена. (мы хотели подсчитать, сколько раз tx был «запущен», и фиксировать, когда 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. Однако, как я уже сказал (и перечислил), есть много других лучших! причины делать упаковочные работы.

Пэт
источник
0

Я знаю, что это старше, но на сегодняшний день нужно отметить 2 пункта в ответе Мэтта Куэлла.

Пункт 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();

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

Тони Ши
источник
Постарайтесь сделать ответы ответом на вопрос, а не ответом на другой ответ. Можно добавить комментарий к ответу Мэтта Куэла, чтобы сказать, что он устарел, но просто напишите свой ответ чисто и правильно.
Кори Кендалл,
-1

Попробуй это:

Query q = sess.createQuery("from Cat cat");
List<?> results = q.list();
for (Object obj : results) {
    Cat cat = (Cat) obj;
}
Брайан Нгуре
источник
4
Это плохая копия ответа Джо Дина , потому что вам все равно нужно что-то делать с catэкземпляром.
Artjom B.
-1

Хорошее решение, чтобы избежать предупреждений о безопасности типов при запросе гибернации, является использование такого инструмента, как TorpedoQuery, который поможет вам создать безопасный тип hql.

Cat cat = from(Cat.class);
org.torpedoquery.jpa.Query<Entity> select = select(cat);
List<Cat> cats = select.list(entityManager);
xjodoin
источник
-1
TypedQuery<EntityName> createQuery = entityManager.createQuery("from EntityName", EntityName.class);
List<EntityName> resultList = createQuery.getResultList();
Ракеш Сингх Балхара
источник
3
Пожалуйста, постарайтесь дать хорошее описание того, как работает ваше решение. См .: Как мне написать хороший ответ? . Спасибо.
Шри
1
Можете ли вы добавить пояснения к своему коду, чтобы другие могли извлечь из него уроки?
Нико Хаасе
-6

Если вы не хотите использовать @SuppressWarnings («не отмечен»), вы можете сделать следующее.

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

К вашему сведению - я создал служебный метод, который делает это за меня, поэтому он не засоряет мой код, и мне не нужно использовать @SupressWarning.

Джо Дин
источник
2
Это просто глупо. Вы добавляете накладные расходы времени выполнения, чтобы полностью решить проблему, связанную с компилятором. Помните, что аргументы типа не повторяются, поэтому проверка типа во время выполнения отсутствует.
Джон Нильссон
Согласен, если вы все еще хотите сделать что-то подобное, вы можете добавить проверку типа во время выполнения с помощью: List <Cat> cats = Collections.checkedList (new ArrayList <Cat> (), Cat.class); cats.addAll (q.list ()); Это должно работать.
ddcruver