จะหลีกเลี่ยงคำเตือนด้านความปลอดภัยประเภท 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
Elazar Leibovich

5
สายเล็กน้อย แต่sess.createQuery("from Cat cat", Cat.class);ตามที่ Elazar กล่าวถึง
Dominik Mohr

คำตอบ:


99

ใช้@SuppressWarningsทุกที่ตามที่แนะนำเป็นวิธีที่ดีที่จะทำมัน q.list()แต่มันไม่เกี่ยวข้องกับบิตของนิ้วมือพิมพ์แต่ละครั้งที่คุณโทร

มีอีกสองเทคนิคที่ฉันแนะนำ:

เขียนผู้ช่วยนักแสดง

เพียงแค่ทำการ refactor ทั้งหมดของคุณ@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 กล่าวว่าจะป้องกันไม่ให้เพิ่มองค์ประกอบที่พิมพ์ไม่ถูกต้องผ่านมุมมอง typesafe ที่เมธอดสร้างขึ้น ไม่มีการตรวจสอบในรายการที่กำหนด
phatblat

11
"รายการ <Cat> list = Collections.checkedList (q.list (), Cat.class);" ยังคงต้องใช้ "@SuppressWarnings" ใน Eclipse เกี่ยวกับเคล็ดลับอื่น ๆ : การพิมพ์ "listAndCast" ไม่ได้สั้นไปกว่า "@SuppressWarnings" ซึ่งเพิ่มโดยอัตโนมัติผ่าน Eclipse
Tristan

2
BTW Collections.checkedList()วิธีการจะไม่ขยายคำเตือนการมอบหมายงานที่ไม่ได้เลือก
Diablo

39

เป็นเวลานานแล้วที่คำถามถูกถาม แต่ฉันหวังว่าคำตอบของฉันอาจเป็นประโยชน์สำหรับคนอย่างฉัน

ถ้าคุณดูที่ javax.persistence เอกสาร APIJava Persistence 2.0คุณจะเห็นว่าบางส่วนวิธีการใหม่ได้รับการเพิ่มมีตั้งแต่ หนึ่งในนั้นคือการที่ผลตอบแทนcreateQuery(String, Class<T>) TypedQuery<T>คุณสามารถใช้TypedQueryเช่นเดียวกับที่คุณใช้Queryกับความแตกต่างเล็กน้อยที่การดำเนินการทั้งหมดจะปลอดภัยในขณะนี้

ดังนั้นเพียงแค่เปลี่ยนรหัสของคุณเป็น smth ดังนี้:

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 () วิธีการใน Hibernate API ไม่ได้พิมพ์ปลอดภัย "โดยการออกแบบ" และมีแผนการที่จะเปลี่ยน

ฉันเชื่อว่าวิธีที่ง่ายที่สุดในการหลีกเลี่ยงคำเตือนของคอมไพเลอร์คือการเพิ่ม @SuppressWarnings ("unchecked") คำอธิบายประกอบนี้สามารถวางไว้ที่ระดับวิธีการหรือหากอยู่ในเมธอดก่อนการประกาศตัวแปร

ในกรณีที่คุณมีเมธอดที่สรุป Query.list () และส่งคืน List (หรือ Collection) คุณจะได้รับคำเตือนด้วย แต่สิ่งนี้ถูกระงับโดยใช้ @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);

ฉันจะได้รับข้อผิดพลาดในการคอมไพล์: Type mismatch: can't convert from List to ArrayList


1
"ไม่มีแผนที่จะเปลี่ยนแปลง" - นั่นเป็นโพสต์จากปี 2005 ฉันจะแปลกใจถ้าสิ่งต่างๆไม่เปลี่ยนแปลงตั้งแต่นั้นมา
Rup

4

ไม่ใช่การกำกับดูแลหรือผิดพลาด คำเตือนดังกล่าวสะท้อนถึงปัญหาพื้นฐานที่แท้จริง - ไม่มีวิธีใดที่คอมไพเลอร์ java สามารถมั่นใจได้ว่าคลาส hibernate จะทำงานได้อย่างถูกต้องและรายการที่ส่งคืนจะมีเฉพาะ Cats เท่านั้น ข้อเสนอแนะใด ๆ ที่นี่ดี


2

ไม่ได้ แต่คุณสามารถแยกออกเป็นวิธีการสืบค้นเฉพาะและระงับคำเตือนด้วย@SuppressWarnings("unchecked")คำอธิบายประกอบ


ผิด ... โจดีนพูดถูกคุณสามารถใช้? เป็นประเภททั่วไปเพื่อหลีกเลี่ยงคำเตือน ...
Mike Stone

1
ที่ไม่เป็นความจริง. หากคุณใช้รายการ <?> คุณจะไม่สามารถใช้องค์ประกอบของรายการเป็นของแมวได้หากไม่มีขั้นตอนที่ไม่จำเป็นในการสร้างรายการซ้ำและส่งแต่ละรายการ
Dave L.

ถ้าคุณใช้ผลลัพธ์โดยตรงผ่านการแคสต์คุณไม่จำเป็นต้องสร้างรายการและไม่ว่าคำถามคือ "มีวิธีหลีกเลี่ยงหรือไม่" คำตอบคือใช่แน่นอนที่สุด (แม้จะไม่มีคำเตือน)
ไมค์ Stone

2

ตอนนี้ Hibernate เวอร์ชันใหม่กว่ารองรับQuery<T>อ็อบเจ็กต์ประเภทที่ปลอดภัยแล้วดังนั้นคุณจึงไม่ต้องใช้@SuppressWarningsหรือใช้แฮ็กเพื่อทำให้คำเตือนของคอมไพเลอร์หายไปอีกต่อไป ในAPI เซสชัน , 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 and Session

โดยเฉพาะ:

  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 มี "unique ()" ซึ่งเป็นเวอร์ชันที่เปิดใช้งานทั่วไปของ Query.uniqueResult () (และเพียงแค่บันทึกปัญหาแทนที่จะโยน ข้อยกเว้น)

นี่เป็นงานที่ต้องทำเพื่อหลีกเลี่ยง @SuppressWarnings อย่างไรก็ตามอย่างที่ฉันพูด (และระบุไว้) มีอีกมากมายที่ดีกว่า! เหตุผลในการทำงานห่อ


0

ฉันรู้ว่าสิ่งนี้เก่ากว่า แต่มี 2 ประเด็นที่ควรทราบ ณ วันนี้ใน Matt Quails Answer

จุดที่ 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();

จะลดคำเตือนอื่น ๆ ลงอย่างเห็นได้ชัดในเครื่องหมายแท็กตอบกลับเดิมถูกเบราว์เซอร์ปล้น


พยายามตอบคำถามไม่ใช่ตอบกลับคำตอบอื่น คุณควรใส่ความคิดเห็นในคำตอบของ Matt Quail เพื่อบอกว่าเขาล้าสมัย แต่เพียงแค่เขียนคำตอบของคุณอย่างถูกต้องและครบถ้วน
Cory Kendall

-1

ลองสิ่งนี้:

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

4
นี่เป็นคำตอบที่ไม่ดีของJoe Deanเนื่องจากคุณยังต้องทำอะไรบางอย่างกับ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
โปรดลองให้คำอธิบายที่ดีเกี่ยวกับวิธีการทำงานของโซลูชันของคุณ ดู: ฉันจะเขียนคำตอบที่ดีได้อย่างไร? . ขอบคุณ.
Shree

1
คุณสามารถเพิ่มคำอธิบายลงในโค้ดเพื่อให้คนอื่นเรียนรู้จากมันได้หรือไม่?
Nico Haase

-6

หากคุณไม่ต้องการใช้ @SuppressWarnings ("unchecked") คุณสามารถทำสิ่งต่อไปนี้ได้

   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 - ฉันสร้างวิธีการใช้งานที่ทำเพื่อฉันดังนั้นมันจึงไม่ทิ้งโค้ดของฉันและฉันไม่ต้องใช้ @SupressWarning


2
แค่นั้นโง่ คุณกำลังเพิ่มค่าใช้จ่ายรันไทม์เพื่อเอาชนะปัญหาที่เกี่ยวข้องกับคอมไพเลอร์อย่างสมบูรณ์ โปรดจำไว้ว่าอาร์กิวเมนต์ประเภทจะไม่ถูก reified ดังนั้นจึงไม่มีการตรวจสอบรันไทม์ของประเภท
John Nilsson

ตกลงถ้าคุณยังต้องการทำสิ่งนี้คุณสามารถเพิ่มการตรวจสอบรันไทม์ของประเภทด้วย: List <Cat> cats = Collections.checkedList (ArrayList ใหม่ <Cat> (), Cat.class); cats.addAll (q.list ()); สิ่งนี้ควรใช้งานได้
ddcruver
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.