我有一个 Spring Boot 应用程序,并使用 Spring Data JPA 查询 MySQL 数据库。
我需要获取使用一些参数过滤的课程列表。
我通常使用语法
param IS NULL or (/*do something with param*/)
,这样如果参数为空,它就会忽略它。
对于简单的数据类型,我没有问题,但是当涉及到对象列表时,我不知道如何检查
NULL
值。如何检查以下查询中的 ?3
参数是否为 NULL
?
@Query("SELECT DISTINCT c FROM Course c\n" +
"WHERE c.courseDate < CURRENT_TIME\n" +
"AND (?1 IS NULL OR (c.id NOT IN (3, 4, 5)))\n" +
"AND (?2 IS NULL OR (c.title LIKE ?2 OR c.description LIKE ?2))\n" +
"AND ((?3) IS NULL OR (c.category IN ?3)) ")
List<Course> getCoursesFiltered(Long courseId, String filter, List<Category> categories);
错误是:
无法提取结果集; SQL [不适用];嵌套异常是 org.hibernate.exception.DataException: 无法提取 ResultSet[SQL: 1241, 21000]
在堆栈跟踪中我可以看到:
原因:java.sql.SQLException:操作数应包含 1 列
如果我的列表包含 3 个元素,生成的查询确实会是
... AND ((?3, ?4, ?5) IS NULL OR (c.category IN (?3, ?4, ?5)))
。但是 IS NULL
不能应用于多个元素(如果我的列表仅包含一个元素,查询可以正常工作)。
我尝试过
size(?3) < 1
、length(?3) < 1
、(?3) IS EMPTY
等,但仍然没有成功。
好吧,半夜醒来后我想到了一件事:
@Query("SELECT DISTINCT c FROM Course c\n" +
"WHERE c.courseDate < CURRENT_TIME\n" +
"AND (?1 IS NULL OR (c.id NOT IN (3, 4, 5)))\n" +
"AND (?2 IS NULL OR (c.title LIKE ?2 OR c.description LIKE ?2))\n" +
"AND (COALESCE(?3) IS NULL OR (c.category IN ?3)) ")
List<Course> getCoursesFiltered(Long courseId, String filter, List<Category> categories);
解决方案很简单,除了
COALESCE
之外还使用 IS NULL
,这样它就可以处理多个值。这样,如果列表至少包含一个非空值,第二个表达式 ((c.category IN ?3)
) 将完成过滤工作。
下次我至少要等一整夜才提问:)
这并不能严格回答您的问题,但一个简单的解决方案是在存储库中使用另一种方法,在调用查询之前检查您的列表,并将它们默认为具有虚拟值的列表。比如:
@Query("SELECT DISTINCT c FROM Course c\n" +
"WHERE c.courseDate < CURRENT_TIME\n" +
"AND (?1 IS NULL OR (c.id NOT IN (3, 4, 5)))\n" +
"AND (?2 IS NULL OR (c.title LIKE ?2 OR c.description LIKE ?2))\n" +
"AND ('DUMMYVALUE' IN ?3 OR (c.category IN ?3)) ")
// make this private to keep it safe
private List<Course> getCoursesFiltered(Long courseId, String filter, List<Category> categories);
// public helper method to put a dummy value in the list that you can check for
public List<Course> getNullableCoursesFiltered(Long courseId, String filter, List<Category> categories) {
if(categories == null) {
categories = Arrays.asList("DUMMYVALUE");
}
return getCoursesFiltered(courseId, filter, categories);
}
Spring 使我们能够使用对象模型作为带有 @Param 注解的查询参数。检查动态查询中列表类型参数是否为空的另一种解决方法是在我们的搜索模型上放置 getter 方法。
假设您有一个名为 PersonSearchFilter 的搜索过滤器模型,您想通过它来搜索 Person 模型;
@Query(value = "SELECT a FROM Person a WHERE 1=1 "
+ "and (:#{#searchFilter.name} is null or a.name like %:#{#searchFilter.name}%) "
+ "and (:#{#searchFilter.statusListSize} = 0 or a.status in (:#{#searchFilter.statusList})) ")
Optional<List<Person>> findByFilter(@Param("searchFilter") PersonSearchFilter searchFilter);
public class PersonSearchFilter {
private String name;
private List<String> statusList;
public String getName() {
return name;
}
public List<String> getStatusList() {
return statusList;
}
public int getStatusListSize() {
return CollectionUtils.isEmpty(statusList) ? 0:statusList.size();
}
}
@Query("SELECT DISTINCT c FROM Course c\n" +
"WHERE c.courseDate < CURRENT_TIME\n" +
"AND (?1 IS NULL OR (c.id NOT IN (3, 4, 5)))\n" +
"AND (?2 IS NULL OR (c.title LIKE ?2 OR c.description LIKE ?2))\n" +
"AND (COALESCE(?3, null) IS NULL OR (c.category IN ?3)) ")
List<Course> getCoursesFiltered(Long courseId, String filter, List<Category> categories);