检查 Spring 数据 JPA 查询中的列表参数是否为空

Check that a List parameter is null in a Spring data JPA query

我有一个 Spring Boot 应用程序并使用 Spring Data JPA 查询 MySQL数据库.

我需要获取使用某些参数过滤的课程列表。

我通常使用语法 param IS NULL or (/*do something with param*/) 以便它忽略参数是否为 null。

对于简单的数据类型,我没有任何问题,但是当涉及到对象列表时,我不知道如何检查 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);

错误是:

could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.DataException: could not extract ResultSet[SQL: 1241, 21000]

在堆栈跟踪中我可以看到:

Caused by: java.sql.SQLException: Operand should contain 1 column(s)

如果我的列表包含 3 个元素,生成的查询确实会是 ... AND ((?3, ?4, ?5) IS NULL OR (c.category IN (?3, ?4, ?5)))。但是 IS NULL 不能应用于多个元素(如果我的列表只包含一个元素,查询工作正常)。

我已经尝试了 size(?3) < 1length(?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 ('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);
}

好的我半夜醒来后想到一件事:

@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);

解决方案是除了 IS NULL 之外还使用 COALESCE,这样它就可以处理多个值。这样一来,如果列表至少包含一个非空值,则第二个表达式 ((c.category IN ?3)) 将完成过滤工作。

下次至少等一晚上再问:)

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();
    }

}