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ąć?

serg
źródło
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.
Matt Quail
źródło
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.

antonpp
źródło
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;
}
cretzel
źródło
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();
shivam oberoi
źródło
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ć.

tyshock
źródło
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

Paulo Merson
źródło
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.

paulmurray
źródło
2

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

Dave L.
źródło
Ź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();
}
David DeMar
źródło
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.

Poklepać
źródło
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ę.

Tony Shih
źródło
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;
}
Brian Ngure
źródło
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);
xjodoin
źródło
-1
TypedQuery<EntityName> createQuery = entityManager.createQuery("from EntityName", EntityName.class);
List<EntityName> resultList = createQuery.getResultList();
Rakesh Singh Balhara
źródło
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.

Joe Dean
źródło
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