我有一个查询,应该根据各种参数进行过滤;这些参数之一是列表。如果列表中有条目,则应基于条目进行过滤;但是如果列表为空/空,则该字段上不应进行任何过滤。
我认为是这样的:
@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);
发送null列表将引发NPE;发送空列表会生成以下SQL,该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)
或使用SpEL:
"and (:#{countryIds.size() > 0} or (a.countryId in (:countryIds))) "
但是同样,它不会编译JPQL。
我想到的唯一解决方案是动态生成难看的JPQL或填充效率低下的countryIds
和siteTypeIds
的所有现有值。
JPA实现是Hibernate,数据库是MySQL。
答案 0 :(得分:0)
对于我来说,针对此类情况的最佳解决方案是Criteria API,如果您不熟悉它,则可以在此处找到一些信息:
答案 1 :(得分:0)
经过反复试验,我找到了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));
答案 2 :(得分:0)
我遇到了同样的问题,因此我在编写扩展解决方案时也使用了嵌入式参数
@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}) ")
其中供应商可以为空,为空或包含值,并且productIdentifier是包含id的嵌入式id,该id传递为
@Param("productIdentifier") ProductIdentifier productIdentifier
间隔也从fromDate到null(永远)或直到untilDate有效。