仅当作为参数传递的List具有元素时,才考虑JPA“where in”

5f0d552i  于 2023-03-13  发布在  其他
关注(0)|答案(4)|浏览(148)

我有一个查询,它应该根据各种参数进行筛选;这些参数之一是列表,如果列表中存在条目,则应该基于条目进行过滤;但如果列表为空/null,则不应对该字段进行任何过滤。
我的想法是这样的:

@Query("select a from Alert a where a.date >= :startDate " +
            "and (((:countryIds) is null) or a.countryId in (:countryIds)) " +
            "and (((:typeIds) is null) or a.siteTypeId in (:typeIds)) ")
List<Alert> findBy(@Param("startDate") Date startDate,
                   @Param("countryIds") Set<Long> countryIds,
                   @Param("typeIds") Set<Long> typeIds);

发送空List会抛出NPE;发送一个空列表时,它会生成以下无效的SQL

where alert0_.date >= '2018-01-01' and                                                       
 ((1, 123) is null or alert0_.countryId in (1, 123))

我也尝试过在JPQL中使用and (((:countryIds) is empty) or a.countryId in (:countryIds)),但在尝试编译JPQL时(在应用程序启动时),它也不起作用:Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: ??? is not mapped at org.hibernate.hql.internal.ast.util.SessionFactoryHelper.requireClassPersister(SessionFactoryHelper.java:171)或使用拼写:"and (:#{countryIds.size() > 0} or (a.countryId in (:countryIds))) ",但同样,它不编译JPQL。
我想到的唯一解决方案是动态生成JPQL,这很难看,或者填充countryIdssiteTypeIds的所有现有值,这效率很低。
JPA实现是Hibernate,数据库是MySQL。

mspsb9vt

mspsb9vt1#

经过大量的试验和错误,我发现一个可以接受的工作解决方案与Spel;认为有些人可能会觉得有用

@Query("select a from Alert a where a.date >= :startDate " 
        "and (:#{#countryIds == null} = true or (a.countryId in (:countryIds))) " +
        "and (:#{#siteTypeIds == null} = true or (a.siteTypeId in (:siteTypeIds))) ")
List<Alert> findBy(@Param("startDate") Date startDate, 
                   @Param("countryIds") Set<Long> countryIds,
                   @Param("siteTypeIds") Set<Long> siteTypeIds);

作为参数发送的集合必须是null,而不是空集。这将生成可接受的SQL:

select alert0_.alertId              as alertId1_0_, [...]
from alert alert0_
where alert0_.date >= '2018-01-01' and
      (0 = 1 or alert0_.countryId in (1, 123)) and
      (1 = 1 or alert0_.siteTypeId in (null));
llmtgqce

llmtgqce2#

我有同样的问题,所以我写扩展的解决方案也使用嵌入式参数

@Query("from PartPrice where "
            + "customer in :#{#customers} and "
            + "( (:#{#suppliers == null || #suppliers.size() == 0} = true and supplier is null) or (:#{#suppliers != null && #suppliers.size() > 0} = true and supplier in :#{#supplier}) ) and "
            + " productIdentifier.manufacturerId = :#{#productIdentifier.manufacturerId} and productIdentifier.productNumber = :#{#productIdentifier.productNumber} and "
            + " ( (:#{#isAbsPrice} = true and abs_price is not null) or (:#{#isAbsPrice} = false and abs_price is null) ) "
            + " and (validUntil is null or validUntil >= :#{#fromDate}) and (:#{#untilDate == null} = true or validFrom <= :#{#untilDate}) ")

其中suppliers可为null、为空或包含值,productIdentifier是包含productNumber和manufacturerId的嵌入ID,传递为

@Param("productIdentifier") ProductIdentifier productIdentifier

间隔也是有效的,从fromDate到null(永远)或untilDate。

8ulbf1ek

8ulbf1ek3#

对于我来说,这种情况下的最佳解决方案是标准API,如果你不熟悉它,你可以在这里找到一些信息:
https://www.objectdb.com/java/jpa/query/criteria

4urapxun

4urapxun4#

经过一些尝试和错误,我得到了这个工作的那些谁想要处理空列表时,使用IN JPA子句.如果有至少一个元素,它过滤正常,否则,它会带来整个结果集,如果集合是空的:

@Query("SELECT h FROM Holiday h WHERE (:#{#ids.empty? T(java.util.Arrays).asList(null) : #ids} is null or h.id in :#{#ids.empty? T(java.util.Arrays).asList(null) : #ids} )")
Optional<List<Holiday>> listAllIn(@Param("ids") Collection<Long> ids);

在Eclipselink 2.7.11.v20220804- 52 dea 2a 3c 0+Spring数据-jpa 2.7.7上运行此代码

相关问题