Giải pháp cho các truy vấn JPQL
Điều này được hỗ trợ cho các truy vấn JPQL trong đặc tả JPA .
Bước 1 : Khai báo một lớp bean đơn giản
package com.path.to;
public class SurveyAnswerStatistics {
private String answer;
private Long cnt;
public SurveyAnswerStatistics(String answer, Long cnt) {
this.answer = answer;
this.count = cnt;
}
}
Bước 2 : Trả về các thể hiện bean từ phương thức kho lưu trữ
public interface SurveyRepository extends CrudRepository<Survey, Long> {
@Query("SELECT " +
" new com.path.to.SurveyAnswerStatistics(v.answer, COUNT(v)) " +
"FROM " +
" Survey v " +
"GROUP BY " +
" v.answer")
List<SurveyAnswerStatistics> findSurveyCount();
}
Ghi chú quan trọng
- Đảm bảo cung cấp đường dẫn đủ điều kiện đến lớp bean, bao gồm cả tên gói. Ví dụ, nếu lớp bean được gọi
MyBean
và nó nằm trong gói com.path.to
, thì đường dẫn đủ điều kiện đến bean sẽ là com.path.to.MyBean
. Việc cung cấp đơn giản MyBean
sẽ không hoạt động (trừ khi lớp bean nằm trong gói mặc định).
- Đảm bảo gọi hàm tạo lớp bean bằng
new
từ khóa. SELECT new com.path.to.MyBean(...)
sẽ hoạt động, trong khi SELECT com.path.to.MyBean(...)
sẽ không.
- Đảm bảo truyền các thuộc tính theo đúng thứ tự như mong đợi trong hàm tạo bean. Cố gắng chuyển các thuộc tính theo một thứ tự khác sẽ dẫn đến một ngoại lệ.
- Đảm bảo rằng truy vấn là một truy vấn JPA hợp lệ, nghĩa là, nó không phải là một truy vấn gốc.
@Query("SELECT ...")
hoặc @Query(value = "SELECT ...")
, hoặc @Query(value = "SELECT ...", nativeQuery = false)
sẽ hoạt động, ngược lại @Query(value = "SELECT ...", nativeQuery = true)
sẽ không hoạt động. Điều này là do các truy vấn gốc được chuyển mà không có sửa đổi đối với nhà cung cấp JPA và được thực thi dựa trên RDBMS cơ bản như vậy. Vì new
và com.path.to.MyBean
không phải là các từ khóa SQL hợp lệ, RDBMS sau đó ném một ngoại lệ.
Giải pháp cho các truy vấn gốc
Như đã lưu ý ở trên, new ...
cú pháp là một cơ chế được JPA hỗ trợ và hoạt động với tất cả các nhà cung cấp JPA. Tuy nhiên, nếu bản thân truy vấn không phải là truy vấn JPA, có nghĩa là, nó là truy vấn gốc, thì new ...
cú pháp sẽ không hoạt động vì truy vấn được chuyển trực tiếp đến RDBMS bên dưới, không hiểu new
từ khóa vì nó không phải là một phần của tiêu chuẩn SQL.
Trong những tình huống như thế này, các lớp bean cần được thay thế bằng các giao diện Spring Data Projection .
Bước 1 : Khai báo giao diện chiếu
package com.path.to;
public interface SurveyAnswerStatistics {
String getAnswer();
int getCnt();
}
Bước 2 : Trả về các thuộc tính dự kiến từ truy vấn
public interface SurveyRepository extends CrudRepository<Survey, Long> {
@Query(nativeQuery = true, value =
"SELECT " +
" v.answer AS answer, COUNT(v) AS cnt " +
"FROM " +
" Survey v " +
"GROUP BY " +
" v.answer")
List<SurveyAnswerStatistics> findSurveyCount();
}
Sử dụng AS
từ khóa SQL để ánh xạ các trường kết quả thành các thuộc tính chiếu để ánh xạ rõ ràng.
Caused by: java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: Unable to locate class [SurveyAnswerReport] [select new SurveyAnswerReport(v.answer,count(v.id)) from com.furniturepool.domain.Survey v group by v.answer] at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1750) at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1677) at org.hibernate.jpa.spi.AbstractEnti..........