Làm thế nào để tránh cảnh báo an toàn kiểu với kết quả Hibernate HQL?


105

Ví dụ, tôi có truy vấn như vậy:

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

Nếu tôi cố gắng làm một cái gì đó như thế này, nó sẽ hiển thị cảnh báo sau

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


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

Có cách nào để tránh nó không?


11
Điều đáng nói là với JPA, bạn có thể có loại truy vấn an toàn bằng cách thêm loại vào createQuery.
Elazar Leibovich

5
Hơi muộn nhưng sess.createQuery("from Cat cat", Cat.class);như Elazar đã đề cập.
Dominik Mohr

Câu trả lời:


99

Sử dụng @SuppressWarningsở mọi nơi, như đã đề xuất, là một cách tốt để làm điều đó, mặc dù nó liên quan đến một chút gõ ngón tay mỗi khi bạn gọi q.list().

Có hai kỹ thuật khác mà tôi muốn đề xuất:

Viết trình trợ giúp diễn viên

Đơn giản chỉ cần cấu trúc lại tất cả của bạn @SuppressWarningsvào một nơi:

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

...

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

Ngăn Eclipse tạo cảnh báo cho các sự cố không thể tránh khỏi

Trong Eclipse, đi tới Window> Preferences> Java> Compiler> Errors / Warnings và bên dưới Generic type, chọn hộp kiểm Ignore unavoidable generic type problems due to raw APIs

Thao tác này sẽ tắt các cảnh báo không cần thiết đối với các sự cố tương tự như sự cố được mô tả ở trên mà không thể tránh khỏi.

Một vài bình luận:

  • Tôi đã chọn để vượt qua Querythay vì kết quả q.list()bởi vì theo cách đó, phương pháp "gian lận" này chỉ có thể được sử dụng để gian lận với Hibernate, và không gian lận bất kỳ Listnói chung.
  • Bạn có thể thêm các phương pháp tương tự cho .iterate()v.v.

20
Thoạt nhìn, phương thức Collections.checkedList (Collection <E>, Class <E>) có vẻ như là giải pháp hoàn hảo. Tuy nhiên, javadoc nói rằng nó chỉ ngăn các phần tử được nhập không chính xác được thêm vào thông qua dạng xem an toàn kiểu mà phương thức tạo ra. Không có kiểm tra nào được thực hiện trong danh sách nhất định.
phatblat 7/10/09

11
"Danh sách <Cat> list = Collections.checkedList (q.list (), Cat.class);" vẫn yêu cầu "@SuppressWarnings" trong Eclipse. Về mẹo khác: gõ "listAndCast" không thực sự ngắn hơn "@SuppressWarnings" được thêm tự động qua Eclipse.
Tristan

2
BTW, Collections.checkedList()phương thức sẽ không nén cảnh báo chuyển nhượng chưa được kiểm tra.
Diablo

39

Câu hỏi được đặt ra đã lâu nhưng tôi hy vọng câu trả lời của tôi có thể hữu ích cho những người như tôi.

Nếu bạn xem các tài liệu javax.persistence api , bạn sẽ thấy rằng một số phương pháp mới đã được thêm vào đó Java Persistence 2.0. Một trong số đó là createQuery(String, Class<T>)trả về TypedQuery<T>. Bạn có thể sử dụng TypedQuerygiống như bạn đã làm với Querysự khác biệt nhỏ đó là tất cả các hoạt động đều an toàn.

Vì vậy, chỉ cần thay đổi mã của bạn thành smth như sau:

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

Và bạn đã sẵn sàng.


1
Câu hỏi không phải là về JPA
Mathijs Segers

2
Các phiên bản gần đây của Hibernate triển khai JPA 2.x, vì vậy câu trả lời này có liên quan.
caspinos,

TypedQuery <T> là kịch bản tốt nhất.
Muneeb Mirza,

21

Chúng tôi cũng sử dụng @SuppressWarnings("unchecked"), nhưng chúng tôi thường cố gắng chỉ sử dụng nó trên khai báo của biến, không phải trên toàn bộ phương thức:

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

15

Cố gắng sử dụng TypedQuerythay vì Query. Ví dụ thay vì thế này: -

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

Dùng cái này:-

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

1
Có cách nào để làm điều này với Criteriamặc dù không?
Stealth Rabbi

5

Trong mã của chúng tôi, chúng tôi chú thích các phương thức gọi với:

@SuppressWarnings ("bỏ chọn")

Tôi biết nó có vẻ giống như một vụ hack, nhưng một nhà đồng phát triển đã kiểm tra gần đây và thấy rằng đó là tất cả những gì chúng tôi có thể làm.


5

Rõ ràng, phương thức Query.list () trong Hibernate API không phải là loại an toàn "theo thiết kế" và không có kế hoạch thay đổi nó .

Tôi tin rằng giải pháp đơn giản nhất để tránh cảnh báo trình biên dịch thực sự là thêm @SuppressWarnings ("bỏ chọn"). Chú thích này có thể được đặt ở cấp phương thức hoặc, nếu bên trong một phương thức, ngay trước khai báo biến.

Trong trường hợp bạn có một phương thức đóng gói Query.list () và trả về Danh sách (hoặc Bộ sưu tập), bạn cũng nhận được một cảnh báo. Nhưng cái này bị chặn bằng cách sử dụng @SuppressWarnings ("rawtypes").

Phương thức listAndCast (Truy vấn) do Matt Quail đề xuất kém linh hoạt hơn so với Query.list (). Trong khi tôi có thể làm:

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

Nếu tôi thử mã bên dưới:

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

Tôi sẽ gặp lỗi biên dịch: Nhập không khớp: không thể chuyển đổi từ Danh sách sang ArrayList


1
"không có kế hoạch thay đổi nó." - đó là một bài đăng từ năm 2005. Tôi sẽ ngạc nhiên nếu mọi thứ không thay đổi kể từ đó.
Rup

4

Nó không phải là một sự giám sát hay một sai lầm. Cảnh báo phản ánh một vấn đề cơ bản thực sự - không có cách nào mà trình biên dịch java thực sự có thể chắc chắn rằng lớp ngủ đông sẽ thực hiện đúng công việc của nó và danh sách mà nó trả về sẽ chỉ chứa Mèo. Bất kỳ đề xuất nào ở đây đều tốt.


2

Không, nhưng bạn có thể tách nó thành các phương thức truy vấn cụ thể và loại bỏ các cảnh báo bằng @SuppressWarnings("unchecked")chú thích.


Sai ... Joe Dean đúng, bạn có thể sử dụng dấu? là loại chung chung để tránh các cảnh báo ...
Mike Stone

1
Đo không phải sự thật. Nếu bạn sử dụng Danh sách <?> Thì bạn không thể sử dụng các phần tử của danh sách như Cat's mà không cần thực hiện bước không cần thiết là tạo danh sách trùng lặp và truyền từng mục.
Dave L.

tốt, nếu bạn sử dụng kết quả trực tiếp thông qua truyền, bạn không cần phải tạo danh sách và bất kể câu hỏi là "có cách nào để tránh nó không", câu trả lời chắc chắn là CÓ (ngay cả khi không có cảnh báo supress)
Mike Stone

2

Các phiên bản mới hơn của Hibernate hiện hỗ trợ một Query<T>đối tượng an toàn kiểu để bạn không còn phải sử dụng @SuppressWarningshoặc thực hiện một số thủ thuật để làm cho các cảnh báo của trình biên dịch biến mất. Trong API phiên , Session.createQuerybây giờ sẽ trả về một Query<T>đối tượng an toàn kiểu . Bạn có thể sử dụng nó theo cách này:

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

Bạn cũng có thể sử dụng nó khi kết quả truy vấn không trả về Cat:

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

Hoặc khi thực hiện một phần lựa chọn:

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

1

Chúng tôi đã có cùng một vấn đề. Nhưng đó không phải là vấn đề lớn đối với chúng tôi vì chúng tôi phải giải quyết các vấn đề lớn khác với Truy vấn và Phiên ngủ đông.

Đặc biệt:

  1. kiểm soát khi nào một giao dịch có thể được cam kết. (chúng tôi muốn đếm số lần tx được "bắt đầu" và chỉ cam kết khi tx được "kết thúc" bằng số lần nó được bắt đầu. Hữu ích cho mã không biết có cần bắt đầu giao dịch hay không. Bây giờ bất kỳ mã nào cần tx chỉ cần "bắt đầu" một mã và kết thúc khi hoàn tất.)
  2. Thu thập số liệu hiệu suất.
  3. Trì hoãn việc bắt đầu giao dịch cho đến khi biết rằng điều gì đó sẽ thực sự được thực hiện.
  4. Hành vi nhẹ nhàng hơn cho query.uniqueResult ()

Vì vậy, đối với chúng tôi, chúng tôi có:

  1. Tạo giao diện (AmplafiQuery) mở rộng Truy vấn
  2. Tạo một lớp (AmplafiQueryImpl) mở rộng AmplafiQuery và bao bọc một org.hibernate.Query
  3. Tạo Txmanager trả về Tx.
  4. Tx có các phương thức createQuery khác nhau và trả về AmplafiQueryImpl

Và cuối cùng,

AmplafiQuery có "asList ()" là phiên bản được kích hoạt chung của Query.list () AmplafiQuery có "unique ()" là phiên bản được kích hoạt chung của Query.uniqueResult () (và chỉ ghi lại sự cố thay vì ghi ngoại lệ)

Đây là rất nhiều công việc để tránh @SuppressWarnings. Tuy nhiên, như tôi đã nói (và liệt kê) có rất nhiều thứ khác tốt hơn! lý do để thực hiện công việc gói.


0

Tôi biết điều này đã cũ hơn nhưng có 2 điểm cần lưu ý cho đến ngày hôm nay trong Matt Quails Answer.

Điểm 1

Điều này

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

Nên là cái này

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

Điểm 2

Từ đây

List list = q.list();

cái này

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

sẽ làm giảm các cảnh báo khác rõ ràng là trong các điểm đánh dấu thẻ trả lời ban đầu đã bị trình duyệt loại bỏ.


Cố gắng biến câu trả lời thành phản hồi cho câu hỏi, không phải phản hồi cho câu trả lời khác. Bạn có thể đưa vào một bình luận về câu trả lời của Matt Quail để nói rằng anh ấy đã lỗi thời, nhưng chỉ cần viết câu trả lời của bạn một cách thuần túy và chính xác.
Cory Kendall

-1

Thử cái này:

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

4
Đây là một bản sao xấu của câu trả lời của Joe Dean , bởi vì bạn vẫn phải làm gì đó với catví dụ.
Artjom B.

-1

Một giải pháp tốt để tránh cảnh báo an toàn kiểu với truy vấn ngủ đông là sử dụng một công cụ như TorpedoQuery để giúp bạn xây dựng hql an toàn kiểu.

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
Hãy cố gắng cung cấp một mô tả đẹp về cách giải pháp của bạn hoạt động. Xem: Làm thế nào để viết một câu trả lời tốt? . Cảm ơn.
Shree

1
Bạn có thể thêm một số giải thích cho bạn mã để những người khác có thể học hỏi từ nó?
Nico Haase,

-6

Nếu bạn không muốn sử dụng @SuppressWarnings ("bỏ chọn"), bạn có thể làm như sau.

   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 - Tôi đã tạo một phương thức sử dụng thực hiện điều này cho tôi để nó không làm hỏng mã của tôi và tôi không phải sử dụng @SupressWarning.


2
Điều đó thật ngu ngốc. Bạn đang thêm chi phí thời gian chạy để khắc phục sự cố hoàn toàn liên quan đến trình biên dịch. Hãy nhớ rằng các đối số kiểu không được sửa lại nên không có kiểm tra thời gian chạy của kiểu.
John Nilsson

Đồng ý, nếu bạn vẫn muốn làm điều gì đó như thế này, bạn có thể thêm kiểu kiểm tra thời gian chạy với: List <Cat> cat = Collections.checkedList (new ArrayList <Cat> (), Cat.class); cat.addAll (q.list ()); Điều này sẽ hoạt động.
ddcruver
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.