Як уникнути попереджувальних попереджень про безпеку з результатами 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 перейдіть у вікно> Налаштування> Java> Компілятор> Помилки / попередження та в розділі Загальний тип встановіть прапорець Ignore unavoidable generic type problems due to raw APIs

Це вимкне непотрібні попередження щодо подібних проблем, як описане вище, які неминучі.

Деякі коментарі:

  • Я вирішив перейти Queryзамість результату, q.list()тому що таким чином цей "обман" може бути використаний лише для обману зі сплячкою, а не для обману Listвзагалі.
  • Ви можете додати подібні методи для .iterate()тощо.

20
На перший погляд, метод Collections. checkedList (Колекція <E>, Клас <E>) виглядає як ідеальне рішення. Однак, javadoc стверджує, що це лише перешкоджає введенню неправильно введених елементів через вигляд typesafe, який створюється методом. Перевірка в цьому списку не проводиться.
пхатблат

11
"Список <Cat> list = Collections. перевірити список (q.list (), Cat.class);" як і раніше вимагає "@SuppressWarnings" у Eclipse. Про іншу пораду: введення "listAndCast" насправді не коротше, ніж "@SuppressWarnings", який автоматично додається через Eclipse.
Трістан

2
BTW, Collections.checkedList()метод не подавить попередження про неперевірене призначення.
Діабло

39

Минуло давно питання, але я сподіваюся, що моя відповідь може бути корисною для когось, як я.

Якщо ви подивитесь на документи javax.persistent 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();

І ви все налаштовані.


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хоча?
Стелс-раббі

5

У нашому коді ми коментуємо методи виклику за допомогою:

@SuppressWarnings ("невірно")

Я знаю, що це схоже на злом, але нещодавно співавтор перевірив і виявив, що ми все можемо зробити.


5

Мабуть, метод Query.list () в API Hibernate не є типовим "дизайн", і не планується його змінювати .

Я вважаю, що найпростішим рішенням уникнути попереджень компілятора є дійсно додати @SuppressWarnings ("невірно"). Ця примітка може бути розміщена на рівні методу або, якщо всередині методу, прямо перед оголошенням змінної.

Якщо у вас є метод, який інкапсулює Query.list () і повертає список (або колекцію), ви також отримуєте попередження. Але цей пригнічується за допомогою @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


1
"не планується її змінювати". - це повідомлення від 2005 року. Я був би здивований, якби все з того часу не змінилося.
Rup

4

Це не недогляд чи помилка. Попередження відображає справжню основну проблему - немає можливості, щоб компілятор java справді був впевнений, що сплячий клас виконає свою роботу належним чином і що список, який він повертає, буде містити лише котів. Будь-яка з пропозицій тут чудова.


2

Ні, але ви можете виділити його в конкретні методи запиту і придушити попередження @SuppressWarnings("unchecked")анотацією.


Неправильно ... Джо Дін має рацію, ви можете скористатися? як загальний тип, щоб уникнути застережень ...
Майк Стоун,

1
Це не правда. Якщо ви використовуєте Список <?>, Ви не можете використовувати елементи списку як Cat без зайвого кроку створення дублюючого списку та кастингу кожного елемента.
Дейв Л.

добре, якщо ви використовуєте результати безпосередньо під час кастингу, вам не потрібно створювати список, і незалежно від того, питання було "чи є спосіб уникнути цього", відповідь, безумовно, ТАК (навіть без подавлення попередження)
Майк Камінь

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

У нас була така ж проблема. Але для нас це не було великим завданням, оскільки нам довелося вирішувати інші більш важливі проблеми із Запитом і сеансом сплячки.

Конкретно:

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

Отже, для нас є:

  1. Створіть інтерфейс (AmplafiQuery), який розширює запит
  2. Створіть клас (AmplafiQueryImpl), який розширює AmplafiQuery і обертає org.hibernate.Query
  3. Створіть Txmanager, який повертає Tx.
  4. Tx має різні методи createQuery і повертає AmplafiQueryImpl

І нарешті,

AmplafiQuery має "asList ()", що є загальною версією Query.list (). AmplafiQuery має "унікальний ()", який є загальною версією Query.uniqueResult () (і просто записує проблему, а не кидати виняток)

Це велика робота, щоб просто уникати @SuppressWarnings. Однак, як я вже сказав (і перераховано), є багато інших кращих! причини робити обгортальну роботу.


0

Я знаю, що це старіше, але на сьогодні слід зазначити два моменти у відповіді на Matt Quails.

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

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


Спробуйте зробити відповіді відповідь на питання, а не відповідь на іншу відповідь. Чудово включити коментар до відповіді Метт Кувай, щоб сказати, що він застарів, але просто напишіть свою відповідь чисто і правильно.
Cory Kendall

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

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

FYI - я створив метод util, який робить це для мене, щоб він не засмітив мій код, і мені не доведеться використовувати @SupressWarning.


2
Це просто нерозумно. Ви додаєте накладні витрати, щоб подолати повну проблему, пов’язану з компілятором. Пам’ятайте, що аргументи типу не повторно повторюються, тому не виконується перевірка типу.
Джон Нільссон

Погоджено, якщо ви все-таки хотіли зробити щось подібне, ви можете додати перевірку часу виконання за допомогою: Список <Cat> cats = Collections. checkedList (новий ArrayList <Cat> (), Cat.class); cats.addAll (q.list ()); Це має спрацювати.
ddcruver
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.