在可为空的引用属性上使用 Spring-Data 和 QueryDSL 进行过滤

Filter with Spring-Data and QueryDSL on nullable reference attribute

我有以下问题。我正在使用 Jquery Datatable 服务器端,我现在正在实现搜索框。但是在特殊情况下我有一个问题,当数据集有一个属性时,"null" 是什么。所以数据集不会被发现,尽管它应该找到因为它在一个属性上匹配。

一开始的情况是这样的。您会看到学徒 Fabio Bartels 有一个数据集,他没有 Fachrichtung。以及 Viktoria 的数据集。

现在当我搜索 Viktoria 时,过滤器按预期工作:

当我搜索 Fabio 时,找不到数据集:

=====

我遇到的问题是,我不知道如何处理过滤器,只有当属性不为空时,才会针对搜索字符串验证属性。

=====

服务器端 Java Class如下所示:

查询Class:

class ContractSearchQuery {

private static QContract contract = QContract.contract;

static BooleanExpression getPredicate(final ContractSearch filter) {
    BooleanExpression predicate;

    if (filter == null || filter.isEmpty()) {
        // SHOW ALL PREDICATE ...
    } else {
        final String search = filter.getSearch();

        final List<BooleanExpression> expressions = new ArrayList<BooleanExpression>();

        // EXPRESSIONS CURRENTLY ONLY ON AUSZUBILDENDER AND FACHRICHTUNG
        // FOR SHOWCASE
        expressions.add(containsApprenticeName(search)); // AUSZUBILDENDER
        expressions.add(containsSpecialisation(search)); // FACHRICHTUNG

        BooleanExpression expression = expressions.get(INTEGER_ZERO);
        for (int i = 1; i < expressions.size(); i++) {
            expression = expression.or(expressions.get(i));
        }

        predicate = expression;
    }
    return predicate;
}

private static BooleanExpression containsApprenticeName(final String search) {
    final BooleanExpression expLastName = contract.apprentice.lastName.containsIgnoreCase(search);
    final BooleanExpression expFirstName = contract.apprentice.firstName.containsIgnoreCase(search);
    return expLastName.or(expFirstName);
}

private static BooleanExpression containsSpecialisation(final String search) {
    return contract.companyOccupationCombination.occupationCombination.specialisation.name.containsIgnoreCase(search);
}

}

Spring-数据存储库调用:

final PageRequest pageRequest = new PageRequest(firstResult / maxResults, maxResults, orderSort);
final Page<Contract> page = contractRepository.findAll(predicate, pageRequest);
return page.getContent();

=======

数据库:

顺便说一句,当我通过加入专业化 Table 对我的数据库进行直接请求时,我认识到,然后我只得到 Fabio 作为记录,当不加入专业化时,我得到了所有三个人。也许与我的问题有关:

select a.first_name, a.last_name from contract c 
    join company_occupation_combination coc on c.company_occupation_combination = coc.id 
    join occupation_combination oc on coc.occupation_combination = oc.id
    join apprentice a on c.apprentice = a.id

结果: "Fabio";"Bartels" "Viktoria";"Kruczek" "Lina";"Ehleiter"

加入:

select a.first_name, a.last_name from contract c 
    join company_occupation_combination coc on         c.company_occupation_combination = coc.id 
    join occupation_combination oc on coc.occupation_combination = oc.id
    join specialisation s on oc.specialisation = s.id 
    join apprentice a on c.apprentice = a.id

结果: "Viktoria";"Kruczek"

====

编辑: 好的,在 db 站点上我发现(使用 Hibernate 和 JPA 我开始忘记 SQL-Basices ;-)),我需要一个左连接用于可空关系,所以我的查询应该导致 sql 喜欢:

select a.first_name, a.last_name from contract c 
join company_occupation_combination coc on c.company_occupation_combination = coc.id 
join occupation_combination oc on coc.occupation_combination = oc.id
left join specialisation s on oc.specialisation = s.id 
join apprentice a on c.apprentice = a.id

====

所以我的问题是,当我有一个使用 QueryDSL 和 Spring-Data-Repository 的 Query-Class 时,如何管理左连接?

如果你真的需要左连接,你不能通过谓词实现(相反可以通过子查询)

为了能够进行左连接,您将需要 JPAQuery。 假设您已经配置了存储库,并且能够使用 EntitiManager,实施 ContractRepositoryCustom ,那么在您的实施中您可以拥有

@PersistenceContext(unitName = "unitname")
protected EntityManager entityManager;

public List<Contract> findAllContracts() {
    return new JPAQuery(entityManager, HQLTemplates.DEFAULT)
            .from(QContract.contract)
            .join(QContract.contract.companyOccupationCombination, QCompanyOccupationCombination.companyOccupationCombination)
            .join(QCompanyOccupationCombination.companyOccupationCombination.occupationCombination, QOccupationCombination.occupationCombination)
            .leftJoin(QOccupationCombination.occupationCombination.specialization, QSpecialization.specialization)
            .join(QSpecialization.specialization.apprentice, QApprentice.apprentice)
            .list(QContract.contract);
}

对于分页,您始终应用 limit(maxResults)offset(firstResult)

我真的很喜欢使用 Spring-Data 和 Query-DSL,因为它使我的代码非常整洁。但我真的很惊讶,对于可空引用的情况似乎没有解决方案。当然,您可以使用另一个解决方案,如提到的@vtorosyan,再次感谢您提供该解决方案,但是当您的项目是结合 QueryDSL 和 Spring-Data 构建的时,您真的不想在其中引入第二种样式你的申请。

但是我需要一个解决方案,所以我现在做了以下事情。

问题的关键是,当我使用来自可空实体的数据时,执行了一个连接,它隐藏了数据集,这些数据集上有空引用,请参见上面的示例。我现在所做的,我希望我不会在该项目的后期使用该解决方案遇到另一个问题。我做了 nullnot null 的引用,并定义了类似 null-record.

的东西

示例我添加了一条专业化记录,例如

ID NAME

0 Keine

我现在使用该记录而不是 null,直到现在具有以下效果:

首先,我的 table 现在显示所有未设置的属性的“Keine”(英文“None”)。使用文本输出然后使用空字符串时看起来更一致。

现在,当我对没有专业化设置的数据记录感兴趣时,我可以明确搜索“Keine”。

而且我的搜索框按预期的方式工作,没有指定的记录。 (这是我从一开始就想解决的问题):

除了该搜索框之外,我还使用模式对话框进行过滤。现在我可以为“可空”记录明确过滤“Keine”:

如果您认为在不使用 Spring-Data 和 QueryDSL 重新构建代码的情况下有另一个解决该问题的好方法,请不要犹豫 post ;-)