在构建 Spring 数据 JPA 规范时使用 HAVING 子句时无法插入空值检查
Unable to insert null checks when using HAVING clause while building Spring Data JPA Specifications
我的代码目前正在使用下面的查询,我正在将查询转换为 JPA 规范。
@Query("SELECT DISTINCT h, SUM(m.annualIncome) " +
"FROM Household h LEFT JOIN h.familyMemberList m " +
"GROUP BY h.id " +
"HAVING SUM(m.annualIncome) < 100000 " +
"AND (:householdSize IS NULL OR COUNT(m) = :householdSize) " +
"AND (:householdIncome IS NULL OR SUM(m.annualIncome) = :householdIncome)")
List<Household> findGrantEligibleHouseholds(@Param("householdSize") long householdSize, @Param("householdIncome") long householdIncome);
这是我目前所做的工作,但以不干净的方式进行。
public static Specification<Household> grantEligibleHouseholdsSpecification(HouseholdCriteria criteria) {
return Specification.where(
(root, query, builder) -> {
List<Predicate> searchCriteria = new ArrayList<>();
final Join<Household, FamilyMember> householdFamilyMemberJoin = root.join(Household_.familyMemberList, JoinType.LEFT);
if(criteria.getHousingType() != null) {
searchCriteria.add(builder.equal(root.get(Household_.housingType), criteria.getHousingType()));
}
query.groupBy(root.get(Household_.id));
if(criteria.getHouseholdIncome() != null && criteria.getHouseholdSize() != null) {
query.having(builder.lt(builder.sum(householdFamilyMemberJoin.get(FamilyMember_.annualIncome)),100000)
,builder.equal(builder.count(householdFamilyMemberJoin),criteria.getHouseholdSize())
,builder.equal(builder.sum(householdFamilyMemberJoin.get(FamilyMember_.annualIncome)),criteria.getHouseholdIncome()));
}
else if(criteria.getHouseholdIncome() != null) {
query.having(builder.lt(builder.sum(householdFamilyMemberJoin.get(FamilyMember_.annualIncome)),100000)
,builder.equal(builder.sum(householdFamilyMemberJoin.get(FamilyMember_.annualIncome)),criteria.getHouseholdIncome() ));
}
else if(criteria.getHouseholdSize() != null) {
query.having(builder.lt(builder.sum(householdFamilyMemberJoin.get(FamilyMember_.annualIncome)),100000)
,builder.equal(builder.count(householdFamilyMemberJoin),criteria.getHouseholdSize()));
}
else {
query.having(builder.lt(builder.sum(householdFamilyMemberJoin.get(FamilyMember_.annualIncome)),100000));
}
query.multiselect();
return builder.and(searchCriteria.toArray(new Predicate[searchCriteria.size()]));
}
);
}
我该如何改进这段代码,以便将来它可以接受更多条件而无需像这样进行如此多的空值检查?谢谢!
您可以使用与 searchCriteria
相同的方法 - 将多个谓词收集到列表中:
final List<Predicate> havingPredicates = new ArrayList<>();
// default predicates
havingPredicates.add(builder.lt(
builder.sum(householdFamilyMemberJoin.get(FamilyMember_.annualIncome)),
100000));
// custom predicates
if (criteria.getHouseholdIncome() != null) {
havingPredicates.add(builder.equal(
builder.sum(householdFamilyMemberJoin.get(FamilyMember_.annualIncome)),
criteria.getHouseholdIncome()));
}
if (criteria.getHouseholdSize() != null) {
havingPredicates.add(builder.equal(
builder.count(householdFamilyMemberJoin),
criteria.getHouseholdSize()));
}
query.having(havingPredicates.toArray(new Predicate[0]));
我的代码目前正在使用下面的查询,我正在将查询转换为 JPA 规范。
@Query("SELECT DISTINCT h, SUM(m.annualIncome) " +
"FROM Household h LEFT JOIN h.familyMemberList m " +
"GROUP BY h.id " +
"HAVING SUM(m.annualIncome) < 100000 " +
"AND (:householdSize IS NULL OR COUNT(m) = :householdSize) " +
"AND (:householdIncome IS NULL OR SUM(m.annualIncome) = :householdIncome)")
List<Household> findGrantEligibleHouseholds(@Param("householdSize") long householdSize, @Param("householdIncome") long householdIncome);
这是我目前所做的工作,但以不干净的方式进行。
public static Specification<Household> grantEligibleHouseholdsSpecification(HouseholdCriteria criteria) {
return Specification.where(
(root, query, builder) -> {
List<Predicate> searchCriteria = new ArrayList<>();
final Join<Household, FamilyMember> householdFamilyMemberJoin = root.join(Household_.familyMemberList, JoinType.LEFT);
if(criteria.getHousingType() != null) {
searchCriteria.add(builder.equal(root.get(Household_.housingType), criteria.getHousingType()));
}
query.groupBy(root.get(Household_.id));
if(criteria.getHouseholdIncome() != null && criteria.getHouseholdSize() != null) {
query.having(builder.lt(builder.sum(householdFamilyMemberJoin.get(FamilyMember_.annualIncome)),100000)
,builder.equal(builder.count(householdFamilyMemberJoin),criteria.getHouseholdSize())
,builder.equal(builder.sum(householdFamilyMemberJoin.get(FamilyMember_.annualIncome)),criteria.getHouseholdIncome()));
}
else if(criteria.getHouseholdIncome() != null) {
query.having(builder.lt(builder.sum(householdFamilyMemberJoin.get(FamilyMember_.annualIncome)),100000)
,builder.equal(builder.sum(householdFamilyMemberJoin.get(FamilyMember_.annualIncome)),criteria.getHouseholdIncome() ));
}
else if(criteria.getHouseholdSize() != null) {
query.having(builder.lt(builder.sum(householdFamilyMemberJoin.get(FamilyMember_.annualIncome)),100000)
,builder.equal(builder.count(householdFamilyMemberJoin),criteria.getHouseholdSize()));
}
else {
query.having(builder.lt(builder.sum(householdFamilyMemberJoin.get(FamilyMember_.annualIncome)),100000));
}
query.multiselect();
return builder.and(searchCriteria.toArray(new Predicate[searchCriteria.size()]));
}
);
}
我该如何改进这段代码,以便将来它可以接受更多条件而无需像这样进行如此多的空值检查?谢谢!
您可以使用与 searchCriteria
相同的方法 - 将多个谓词收集到列表中:
final List<Predicate> havingPredicates = new ArrayList<>();
// default predicates
havingPredicates.add(builder.lt(
builder.sum(householdFamilyMemberJoin.get(FamilyMember_.annualIncome)),
100000));
// custom predicates
if (criteria.getHouseholdIncome() != null) {
havingPredicates.add(builder.equal(
builder.sum(householdFamilyMemberJoin.get(FamilyMember_.annualIncome)),
criteria.getHouseholdIncome()));
}
if (criteria.getHouseholdSize() != null) {
havingPredicates.add(builder.equal(
builder.count(householdFamilyMemberJoin),
criteria.getHouseholdSize()));
}
query.having(havingPredicates.toArray(new Predicate[0]));