在Spring数据JPA查询中检查List参数是否为空

时间:2019-01-03 17:17:55

标签: sql jpa spring-data-jpa spring-data hql

我有一个 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);

错误是:

  

无法提取ResultSet; SQL [n / a];嵌套的异常是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) < 1length(?3) < 1(?3) IS EMPTY等,但还是没有运气。

3 个答案:

答案 0 :(得分:2)

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

}

答案 1 :(得分:1)

这不能严格回答您的问题,但是一个简单的解决方案是在存储库中提供另一种方法,该方法在调用查询之前检查您的列表,并将它们默认为具有伪值的列表。像这样:

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

答案 2 :(得分:1)

好的,我在半夜醒来后还是有事:

@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))将完成过滤工作。

我下次至少要等一整夜才问一个问题:)