Jak uniknąć ostrzeżeń bezpieczeństwa typów dzięki wynikom Hibernate HQL?


105

Na przykład mam takie zapytanie:

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

Jeśli spróbuję zrobić coś takiego, pojawia się następujące ostrzeżenie

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


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

Czy jest sposób, aby tego uniknąć?


11
Warto wspomnieć, że w przypadku JPA można mieć bezpieczne zapytania typu, dodając typ do createQuery.
Elazar Leibovich,

5
Trochę późno, ale sess.createQuery("from Cat cat", Cat.class);jak wspomniał Elazar.
Dominik Mohr,

Odpowiedzi:


99

@SuppressWarningsJak sugerowano, używanie wszędzie jest dobrym sposobem, chociaż wymaga odrobiny pisania palcem za każdym razem, gdy dzwonisz q.list().

Sugeruję dwie inne techniki:

Napisz pomocnika obsady

Po prostu zrefaktoryzuj wszystko @SuppressWarningsw jednym miejscu:

List<Cat> cats = MyHibernateUtils.listAndCast(q);

...

public static <T> List<T> listAndCast(Query q) {
    @SuppressWarnings("unchecked")
    List list = q.list();
    return list;
}

Zapobiegaj generowaniu przez Eclipse ostrzeżeń o nieuniknionych problemach

W Eclipse przejdź do Okno> Preferencje> Java> Kompilator> Błędy / ostrzeżenia i w obszarze Typ ogólny zaznacz pole wyboru Ignore unavoidable generic type problems due to raw APIs

Spowoduje to wyłączenie niepotrzebnych ostrzeżeń o podobnych problemach, takich jak opisany powyżej, których nie można uniknąć.

Kilka komentarzy:

  • Zdecydowałem się przekazać Queryzamiast wynikuq.list() ponieważ w ten sposób ta metoda „oszukiwania” może być używana tylko do oszukiwania za pomocą Hibernate, a nie Listogólnie do oszukiwania .
  • Możesz dodać podobne metody .iterate()itp.

20
Na pierwszy rzut oka metoda Collections.checkedList (Collection <E>, Class <E>) wygląda na idealne rozwiązanie. Jednak javadoc mówi, że zapobiega tylko dodawaniu niepoprawnie wpisanych elementów za pośrednictwem widoku bezpiecznego dla typów, który generuje metoda. Podana lista nie jest sprawdzana.
phatblat

11
"List <Cat> list = Collections.checkedList (q.list (), Cat.class);" nadal wymaga „@SuppressWarnings” w Eclipse. O drugiej wskazówce: wpisanie „listAndCast” nie jest tak naprawdę krótsze niż „@SuppressWarnings”, które jest dodawane automatycznie przez Eclipse.
Tristan

2
BTW, Collections.checkedList()metoda nie usunie ostrzeżenia o niezaznaczonym przypisaniu.
Diablo

39

Minęło dużo czasu, odkąd zadano to pytanie, ale mam nadzieję, że moja odpowiedź może być pomocna dla kogoś takiego jak ja.

Jeśli spojrzysz na dokumentację javax.persistence api , zobaczysz, że od tamtej pory dodano tam kilka nowych metod Java Persistence 2.0. Jednym z nich jest createQuery(String, Class<T>)powrót TypedQuery<T>. Możesz używać TypedQuerytak, jak to zrobiłeśQuery z tą małą różnicą, że wszystkie operacje są teraz bezpieczne.

Więc po prostu zmień swój kod na coś takiego:

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

I wszystko gotowe.


1
Pytanie nie dotyczy JPA
Mathijs Segers,

2
Najnowsze wersje Hibernate'a implementują JPA 2.x, więc ta odpowiedź jest istotna.
caspinos

TypedQuery <T> to najlepszy scenariusz.
Muneeb Mirza

21

My też używamy @SuppressWarnings("unchecked"), ale najczęściej staramy się używać go tylko na deklaracji zmiennej, a nie na metodzie jako całości:

public List<Cat> findAll() {
    Query q = sess.createQuery("from Cat cat");
    @SuppressWarnings("unchecked")
    List<Cat> cats = q.list();
    return cats;
}

15

Spróbuj użyć TypedQueryzamiast Query. Na przykład zamiast tego: -

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

Użyj tego:-

TypedQuery<Cat> q1 = sess.createQuery("from Cat cat", Cat.class);
List<Cat> cats = q1.list();

1
Czy jest jednak sposób, aby to zrobić Criteria?
Stealth Rabbi

5

W naszym kodzie opisujemy metody wywołujące:

@SuppressWarnings („niezaznaczone”)

Wiem, że wygląda to na włamanie, ale współtwórca sprawdził ostatnio i stwierdził, że to wszystko, co możemy zrobić.


5

Najwyraźniej metoda Query.list () w interfejsie Hibernate API nie jest bezpieczna pod względem typu „z założenia” i nie ma planów jej zmiany .

Uważam, że najprostszym sposobem uniknięcia ostrzeżeń kompilatora jest rzeczywiście dodanie @SuppressWarnings („niezaznaczone”). Można umieścićadnotację na poziomie metody lub, jeśli znajduje się wewnątrz metody, tuż przed deklaracją zmiennej.

Jeśli masz metodę, która hermetyzuje Query.list () i zwraca List (lub Collection), również otrzymasz ostrzeżenie. Ale ten jest pomijany za pomocą @SuppressWarnings ("rawtypes").

Metoda listAndCast (Query) zaproponowana przez Matta Quaila jest mniej elastyczna niż Query.list (). Chociaż mogę:

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

Jeśli spróbuję poniższy kod:

Query q = sess.createQuery("from Cat cat");
ArrayList<Cat> cats = MyHibernateUtils.listAndCast(q);

Wystąpi błąd kompilacji: Niezgodność typu: nie można przekonwertować z listy na ArrayList


1
„nie ma planów, aby to zmienić”. - to post z 2005 roku. Zdziwiłbym się, gdyby od tamtego czasu nic się nie zmieniło.
Rup

4

To nie jest przeoczenie ani błąd. Ostrzeżenie odzwierciedla rzeczywisty podstawowy problem - nie ma możliwości, aby kompilator java mógł naprawdę mieć pewność, że klasa hibernacji wykona swoje zadanie poprawnie i że lista, którą zwróci, będzie zawierała tylko Cats. Każda z poniższych sugestii jest w porządku.


2

Nie, ale można go wyodrębnić do określonych metod zapytań i pominąć ostrzeżenia za pomocą @SuppressWarnings("unchecked")adnotacji.


Źle ... Joe Dean ma rację, możesz użyć? jako typowy typ, aby uniknąć ostrzeżeń ...
Mike Stone

1
To nieprawda. Jeśli używasz listy <?>, Nie możesz używać elementów listy jako Cat bez niepotrzebnego kroku tworzenia zduplikowanej listy i rzutowania każdej pozycji.
Dave L.

cóż, jeśli korzystasz z wyników bezpośrednio przez casting, nie musisz tworzyć listy, a mimo to pytanie brzmiało „czy jest sposób, aby tego uniknąć”, odpowiedź jest zdecydowanie TAK (nawet bez ostrzeżeń o supresach)
Mike Stone

2

Nowsze wersje Hibernacji obsługują teraz Query<T>obiekt bezpieczny dla typu, więc nie musisz już używać @SuppressWarningsani implementować hackowania, aby ostrzeżenia kompilatora zniknęły. W API sesji , Session.createQuerybędą teraz zwracać typ bezpieczny Query<T>obiekt. Możesz to wykorzystać w ten sposób:

Query<Cat> query = session.createQuery("FROM Cat", Cat.class);
List<Cat> cats = query.list();

Możesz go również użyć, gdy wynik zapytania nie zwróci Cat:

public Integer count() {
    Query<Integer> query = sessionFactory.getCurrentSession().createQuery("SELECT COUNT(id) FROM Cat", Integer.class);
    return query.getSingleResult();
}

Lub podczas częściowego wyboru:

public List<Object[]> String getName() {
    Query<Object[]> query = sessionFactory.getCurrentSession().createQuery("SELECT id, name FROM Cat", Object[].class);
    return query.list();
}

1

Mieliśmy ten sam problem. Ale to nie była dla nas wielka sprawa, ponieważ musieliśmy rozwiązać inne, bardziej poważne problemy z Hibernate Query and Session.

Konkretnie:

  1. kontrola, kiedy transakcja może zostać zatwierdzona. (Chcieliśmy policzyć, ile razy tx był „uruchamiany” i zatwierdzać tylko taką samą liczbę razy, gdy tx został „zakończony”, jak został uruchomiony. Przydatne w przypadku kodu, który nie wie, czy musi rozpocząć transakcję. Teraz każdy kod, który potrzebuje tx, po prostu „zaczyna” jeden i kończy go po zakończeniu).
  2. Gromadzenie wskaźników wydajności.
  3. Opóźnianie rozpoczęcia transakcji, aż będzie wiadomo, że coś faktycznie zostanie zrobione.
  4. Bardziej delikatne zachowanie dla query.uniqueResult ()

Więc dla nas mamy:

  1. Utwórz interfejs (AmplafiQuery), który rozszerzy Query
  2. Utwórz klasę (AmplafiQueryImpl), która rozszerza AmplafiQuery i otacza org.hibernate.Query
  3. Utwórz Txmanager, który zwraca Tx.
  4. Tx ma różne metody createQuery i zwraca AmplafiQueryImpl

I wreszcie

AmplafiQuery ma "asList ()", która jest generyczną włączoną wersją Query.list () AmplafiQuery ma "unique ()", czyli generyczną włączoną wersję Query.uniqueResult () (i po prostu rejestruje problem, zamiast wyrzucać wyjątek)

To dużo pracy, jeśli chodzi o unikanie @SuppressWarnings. Jednak, jak powiedziałem (i wymieniłem), jest wiele innych lepszych! powody, dla których warto wykonać owijanie.


0

Wiem, że to jest starsze, ale na dzień dzisiejszy należy odnotować 2 punkty w odpowiedzi Matt Quails.

Punkt 1

To

List<Cat> cats = Collections.checkedList(Cat.class, q.list());

Powinien być taki

List<Cat> cats = Collections.checkedList(q.list(), Cat.class);

Punkt 2

Od tego

List list = q.list();

do tego

List<T> list = q.list();

zredukowałby inne ostrzeżenia, oczywiście w oryginalnych znacznikach znaczników odpowiedzi zostały usunięte przez przeglądarkę.


Postaraj się, aby odpowiedzi były odpowiedzią na pytanie, a nie odpowiedzią na inną odpowiedź. Dobrze jest dołączyć komentarz do odpowiedzi Matta Quaila, aby powiedzieć, że jest nieaktualny, ale po prostu napisz odpowiedź czysto i poprawnie.
Cory Kendall

-1

Spróbuj tego:

Query q = sess.createQuery("from Cat cat");
List<?> results = q.list();
for (Object obj : results) {
    Cat cat = (Cat) obj;
}

4
To jest zła kopia odpowiedzi Joe Deana , ponieważ nadal musisz coś zrobić z catinstancją.
Artjom B.,

-1

Dobrym rozwiązaniem, aby uniknąć ostrzeżeń dotyczących bezpieczeństwa typów za pomocą zapytania hibernacji, jest użycie narzędzia takiego jak TorpedoQuery, które pomoże Ci zbudować bezpieczny typ 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
Spróbuj podać miły opis, jak działa Twoje rozwiązanie. Zobacz: Jak napisać dobrą odpowiedź? . Dzięki.
Shree

1
Czy możesz dodać do swojego kodu jakieś wyjaśnienie, tak aby inni mogli się z niego nauczyć?
Nico Haase

-6

Jeśli nie chcesz używać @SuppressWarnings („niezaznaczone”), możesz wykonać następujące czynności.

   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 - stworzyłem metodę util, która robi to za mnie, więc nie zaśmieca mojego kodu i nie muszę używać @SupressWarning.


2
To po prostu głupie. Dodajesz obciążenie środowiska uruchomieniowego, aby rozwiązać problem całkowicie związany z kompilatorem. Pamiętaj, że argumenty typu nie są reifikowane, więc nie ma sprawdzania typu w czasie wykonywania.
John Nilsson,

Zgoda, jeśli nadal chcesz zrobić coś takiego, możesz dodać sprawdzanie typu w czasie wykonywania za pomocą: List <Cat> cats = Collections.checkedList (new ArrayList <Cat> (), Cat.class); cats.addAll (q.list ()); To powinno działać.
ddcruver
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.