按其元素的多个属性过滤集合 - QueryDSL

时间:2016-08-19 15:51:14

标签: java hibernate querydsl

我正在研究基于QueryDSL的动态过滤器组件,并使用SpringData进行查询执行。因此,我从收到的数据ad中创建了Predicate个实例,并将其传递给QueryDslPredicateExecutor。对于对实体属性的动态访问,我使用输入到实体类的通用PathBuilder

考虑以下(简化)代码:

class Offer {
    List<LanguageToName> names;
}
class LanguageToName {
    String name;
    String language;
}

当我尝试查询具有属性'abc'的集合Offer元素中的name个entites时,我只需按如下方式创建谓词:

pathBuilder.getCollection("names", LanguageToName.class).any().getString("name")
    .like("%" + fieldData.getFieldValue() + "%");

但是,我无法想出一个解决方案,可以使用PathBuilder通过包含对象的多个属性来过滤集合。当我使用.and()附加上面的代码并通过pathBuilder变量再次访问集合时,我自然会得到相当于使用AND EXISTS...附加sql查询的结果,这不是所需的结果。我还尝试使用getCollection().contains(),但我无法创建描述此类案例的Expression<LanguageToName>

有没有办法创建一个Predicate,它会根据集合中元素的多个属性过滤实体,这是查询实体的一个字段?

2 个答案:

答案 0 :(得分:0)

我在项目中遇到了同样的问题。 我的解决方法是手动构建存在的子查询。

假设您的两个类都映射为实体:

@Entity
@Table(name = "Offer")
public class Offer {

    @Id
    String id;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "offer")
    List<LanguageToName> names;
}

@Entity
@Table(schema = "dcsdba", name = "Language_To_Name")
public class LanguageToName {

    @Id
    String id;

    @ManyToOne(fetch= FetchType.LAZY)
    @JoinColumn(name="Offer_id")
    private Offer offer;

    String name;
    String language;
}

使用any()的简单查询:

BooleanExpression namesFilter = QOffer.offer.names.any().name.eq("Esperanto");

映射到

select
    offer0_.id as id1_7_ 
from
    offer offer0_ 
where
    exists (
        select
            1 
        from
            dcsdba.language_to_name names1_ 
        where
            offer0_.id=names1_.offer_id 
            and names1_.name=?
    )

子查询:

BooleanExpression namesFilter = JPAExpressions.selectOne()
            .from(languageToName)
            .where(languageToName.offer.eq(QOffer.offer)
                    .and(languageToName.name.eq("Esperanto")))
            .exists();

地图:

select
    offer0_.id as id1_7_ 
from
    offer offer0_ 
where
    exists (
        select
            1 
        from
            dcsdba.language_to_name languageto1_ 
        where
            languageto1_.offer_id=offer0_.id 
            and languageto1_.name=?
    )

完全匹配以前的SQL。 您可以添加其他条件,如:

BooleanExpression namesFilter = JPAExpressions.selectOne()
            .from(languageToName)
            .where(languageToName.offer.eq(QOffer.offer)
                    .and(languageToName.name.eq("Esperanto"))
                    .and(languageToName.language.like("E%")))
            .exists();

答案 1 :(得分:0)

我遇到了类似的问题,最后用子查询解决了这个问题(但是,在我看来,它仅适用于1级嵌套)。

我最初的谓词是(它正在进行2个独立的子查询):

Predicate predicate = codeTable.customer.id.eq(customerId)
                .and(codeTable.qualifierResults.any().customerQualifier.type.eq("TARGET_TYPE"))
                .and(codeTable.qualifierResults.any().customerQualifier.referenceType.code.eq("TARGET_CODE"));

但是我最终得到的正确谓词是:

BooleanExpression customerQualifierCondition = JPAExpressions
        .selectFrom(codeTableQualifierResult)
        .where(codeTableQualifierResult.in(codeTable.qualifierResults),
                codeTableQualifierResult.customerQualifier.type.eq("TARGET_TYPE"),
                codeTableQualifierResult.customerQualifier.referenceType.code.eq("TARGET_CODE"))
        .exists();
Predicate predicate = codeTable.customer.id.eq(customerId).and(customerQualifierCondition);

想法是编写1个单独的子查询,在其中您一次应用所有必要条件(而不是将它们单独应用于您的集合)。