Bagaimana cara menghindari peringatan keamanan tipe dengan hasil Hibernate HQL?

105

Misalnya saya punya pertanyaan seperti itu:

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

Jika saya mencoba membuat sesuatu seperti ini, itu menunjukkan peringatan berikut

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


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

Apakah ada cara untuk menghindarinya?

serg
sumber
11
Perlu disebutkan bahwa dengan JPA Anda dapat mengetik kueri yang aman, dengan menambahkan tipe tersebut ke createQuery.
Elazar Leibovich
5
Sedikit terlambat tapi sess.createQuery("from Cat cat", Cat.class);seperti yang disebutkan Elazar.
Dominik Mohr

Jawaban:

99

Menggunakan di @SuppressWarningsmana-mana, seperti yang disarankan, adalah cara yang baik untuk melakukannya, meskipun ini melibatkan sedikit ketikan jari setiap kali Anda menelepon q.list().

Ada dua teknik lain yang saya sarankan:

Menulis pembantu pemeran

Cukup refactor semua Anda @SuppressWarningsmenjadi satu tempat:

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

...

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

Mencegah Eclipse membuat peringatan untuk masalah yang tidak terhindarkan

Di Eclipse, buka Window> Preferences> Java> Compiler> Errors / Warnings dan di bawah Generic type, pilih kotak centang Ignore unavoidable generic type problems due to raw APIs

Ini akan mematikan peringatan yang tidak perlu untuk masalah serupa seperti yang dijelaskan di atas yang tidak dapat dihindari.

Beberapa komentar:

  • Saya memilih untuk meneruskan Querybukan hasil q.list()karena dengan cara itu metode "curang" ini hanya dapat digunakan untuk menipu dengan Hibernate, dan bukan untuk curang Listsecara umum.
  • Anda dapat menambahkan metode serupa untuk .iterate()dll.
Matt Puyuh
sumber
20
Pada pandangan pertama, metode Collections.checkedList (Collection <E>, Class <E>) terlihat seperti solusi yang tepat. Namun, javadoc mengatakan bahwa hal itu hanya mencegah penambahan elemen yang salah ketik melalui tampilan aman jenis yang dihasilkan metode. Tidak ada pemeriksaan yang dilakukan pada daftar yang diberikan.
phatblat
11
"Daftar <Cat> list = Collections.checkedList (q.list (), Cat.class);" masih membutuhkan "@SuppressWarnings" di Eclipse. Tentang tip lainnya: mengetik "listAndCast" tidak lebih pendek dari "@SuppressWarnings" yang ditambahkan secara otomatis melalui Eclipse.
Tristan
2
BTW, Collections.checkedList()metode tidak akan menekan peringatan tugas yang tidak dicentang.
Diablo
39

Sudah lama sejak pertanyaan itu diajukan, tetapi saya harap jawaban saya dapat membantu orang seperti saya.

Jika Anda melihat dokumen javax.persistence api , Anda akan melihat bahwa beberapa metode baru telah ditambahkan di sana sejak itu Java Persistence 2.0. Salah satunya adalah createQuery(String, Class<T>)yang kembali TypedQuery<T>. Anda dapat menggunakan TypedQueryseperti yang Anda lakukan dengan Queryperbedaan kecil bahwa semua operasi dikategorikan aman sekarang.

Jadi, ubah saja kode Anda menjadi seperti ini:

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

Dan Anda sudah siap.

antonpp
sumber
1
Pertanyaan bukan tentang JPA
Mathijs Segers
2
Versi terbaru Hibernate mengimplementasikan JPA 2.x, jadi jawaban ini relevan.
caspinos
TypedQuery <T> adalah skenario terbaik.
Muneeb Mirza
21

Kami juga menggunakan @SuppressWarnings("unchecked"), tetapi kami paling sering mencoba menggunakannya hanya pada deklarasi variabel, bukan pada metode secara keseluruhan:

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

Coba gunakan, TypedQuerybukan Query. Sebagai contoh, bukan ini: -

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

Gunakan ini:-

TypedQuery<Cat> q1 = sess.createQuery("from Cat cat", Cat.class);
List<Cat> cats = q1.list();
shivam oberoi
sumber
1
Apakah ada cara untuk melakukan ini Criteria?
Stealth Rabbi
5

Dalam kode kami, kami menganotasi metode pemanggilan dengan:

@Suppressings ("tidak ada")

Saya tahu ini tampak seperti peretasan, tetapi rekan pengembang baru-baru ini memeriksa dan menemukan hanya itu yang dapat kami lakukan.

tyshock
sumber
5

Rupanya, metode Query.list () dalam Hibernate API bukan tipe aman "menurut desain", dan tidak ada rencana untuk mengubahnya .

Saya yakin solusi paling sederhana untuk menghindari peringatan compiler adalah dengan menambahkan @SuppressWarnings ("unchecked"). Anotasi ini dapat ditempatkan di tingkat metode atau, jika di dalam metode, tepat sebelum deklarasi variabel.

Jika Anda memiliki metode yang merangkum Query.list () dan mengembalikan List (atau Collection), Anda juga mendapatkan peringatan. Tapi yang ini ditekan menggunakan @SuppressWarnings ("rawtypes").

Metode listAndCast (Query) yang diusulkan oleh Matt Quail kurang fleksibel dibandingkan Query.list (). Sementara saya bisa melakukan:

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

Jika saya coba kode di bawah ini:

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

Saya akan mendapatkan kesalahan kompilasi: Type mismatch: can't convert from List to ArrayList

Paulo Merson
sumber
1
"tidak ada rencana untuk mengubahnya." - itu adalah kiriman dari tahun 2005. Saya akan terkejut jika keadaan tidak berubah sejak saat itu.
Rup
4

Ini bukan kekeliruan atau kesalahan. Peringatan tersebut mencerminkan masalah mendasar yang sebenarnya - tidak mungkin compiler java benar-benar yakin bahwa kelas hibernate akan melakukan tugasnya dengan benar dan bahwa daftar yang dikembalikan hanya akan berisi Cats. Semua saran di sini baik-baik saja.

paulmurray
sumber
2

Tidak, tetapi Anda dapat mengisolasinya ke dalam metode kueri tertentu dan menyembunyikan peringatan dengan @SuppressWarnings("unchecked")anotasi.

Dave L.
sumber
Salah ... Joe Dean benar, Anda dapat menggunakan? sebagai tipe umum untuk menghindari peringatan ...
Mike Stone
1
Itu tidak benar. Jika Anda menggunakan List <?> Maka Anda tidak dapat menggunakan elemen list sebagai Cat tanpa langkah yang tidak perlu untuk membuat daftar duplikat dan mentransmisikan setiap item.
Dave L.
Nah, jika Anda menggunakan hasil langsung melalui casting Anda tidak perlu membuat daftar, dan terlepas dari itu, pertanyaannya adalah "adakah cara untuk menghindarinya", jawabannya pasti YA (bahkan tanpa peringatan supress)
Mike Batu
2

Versi Hibernate yang lebih baru sekarang mendukung jenis Query<T>objek aman sehingga Anda tidak perlu lagi menggunakan @SuppressWarningsatau menerapkan beberapa peretasan untuk menghilangkan peringatan compiler. Di Session API , Session.createQuerysekarang akan mengembalikan jenis Query<T>objek aman . Anda dapat menggunakannya dengan cara ini:

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

Anda juga bisa menggunakannya saat hasil kueri tidak mengembalikan Cat:

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

Atau saat melakukan pemilihan parsial:

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

Kami memiliki masalah yang sama. Tapi itu bukan masalah besar bagi kami karena kami harus menyelesaikan masalah lain yang lebih besar dengan Hibernate Query and Session.

Secara khusus:

  1. mengontrol kapan transaksi dapat dilakukan. (kami ingin menghitung berapa kali tx "dimulai" dan hanya melakukan ketika tx "diakhiri" dengan frekuensi yang sama saat dimulai. Berguna untuk kode yang tidak tahu apakah perlu memulai transaksi. Sekarang kode apa pun yang membutuhkan tx hanya "memulai" satu dan mengakhirinya setelah selesai.)
  2. Pengumpulan metrik kinerja.
  3. Menunda memulai transaksi sampai diketahui bahwa sesuatu akan benar-benar dilakukan.
  4. Perilaku yang lebih lembut untuk query.uniqueResult ()

Jadi bagi kami, kami memiliki:

  1. Buat antarmuka (AmplafiQuery) yang memperluas Query
  2. Buat kelas (AmplafiQueryImpl) yang memperluas AmplafiQuery dan membungkus org.hibernate.Query
  3. Buat Txmanager yang mengembalikan Tx.
  4. Tx memiliki berbagai metode createQuery dan mengembalikan AmplafiQueryImpl

Dan terakhir,

AmplafiQuery memiliki "asList ()" yang merupakan versi yang diaktifkan secara generik dari Query.list () AmplafiQuery memiliki "unique ()" yang merupakan versi yang diaktifkan secara generik dari Query.uniqueResult () (dan hanya mencatat masalah daripada melontarkan pengecualian)

Ini banyak pekerjaan hanya untuk menghindari @SuppressWarnings. Namun, seperti yang saya katakan (dan terdaftar) ada banyak lainnya yang lebih baik! alasan untuk melakukan pekerjaan pembungkus.

Menepuk
sumber
0

Saya tahu ini lebih tua tetapi 2 hal yang perlu diperhatikan untuk hari ini di Matt Quails Answer.

Poin 1

Ini

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

Seharusnya begini

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

Butir 2

Dari ini

List list = q.list();

untuk ini

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

akan mengurangi peringatan lain secara jelas di reply original reply markers sudah di hapus oleh browser.

Tony Shih
sumber
Cobalah untuk membuat jawaban menjadi respons atas pertanyaan, bukan respons untuk jawaban lain. Tidak masalah untuk memasukkan komentar pada jawaban Matt Quail untuk mengatakan bahwa dia sudah ketinggalan zaman, tetapi tulis saja jawaban Anda dengan murni dan benar.
Cory Kendall
-1

Coba ini:

Query q = sess.createQuery("from Cat cat");
List<?> results = q.list();
for (Object obj : results) {
    Cat cat = (Cat) obj;
}
Brian Ngure
sumber
4
Ini salinan buruk dari jawaban Joe Dean , karena Anda masih harus melakukan sesuatu dengan catcontoh itu.
Artjom B.
-1

Solusi yang baik untuk menghindari peringatan keamanan tipe dengan kueri hibernasi adalah dengan menggunakan alat seperti TorpedoQuery untuk membantu Anda membangun HQL yang aman.

Cat cat = from(Cat.class);
org.torpedoquery.jpa.Query<Entity> select = select(cat);
List<Cat> cats = select.list(entityManager);
xjodoin
sumber
-1
TypedQuery<EntityName> createQuery = entityManager.createQuery("from EntityName", EntityName.class);
List<EntityName> resultList = createQuery.getResultList();
Rakesh Singh Balhara
sumber
3
Harap coba berikan deskripsi yang bagus tentang cara kerja solusi Anda. Lihat: Bagaimana cara menulis jawaban yang baik? . Terima kasih.
Shree
1
Dapatkah Anda menambahkan penjelasan pada kode Anda sehingga orang lain dapat mempelajarinya?
Nico Haase
-6

Jika Anda tidak ingin menggunakan @SuppressWarnings ("tidak dicentang"), Anda dapat melakukan hal berikut.

   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 - Saya membuat metode util yang melakukan ini untuk saya sehingga tidak mengotori kode saya dan saya tidak perlu menggunakan @SupressWarning.

Joe Dean
sumber
2
Itu bodoh. Anda menambahkan overhead waktu proses untuk mengatasi masalah yang sepenuhnya terkait dengan compiler. Ingatlah bahwa argumen tipe tidak direifikasi sehingga tidak ada pemeriksaan runtime untuk tipe tersebut.
John Nilsson
Setuju, jika Anda masih ingin melakukan sesuatu seperti ini, Anda dapat menambahkan pemeriksaan runtime tipe dengan: List <Cat> cats = Collections.checkedList (new ArrayList <Cat> (), Cat.class); cats.addAll (q.list ()); Ini seharusnya berhasil.
ddcruver