使用AND运算符CriteriaBuilder进行规范和谓词收集

时间:2014-07-13 23:46:36

标签: java spring junit jpa-2.0 spring-data

为了从我的数据库中获取申请人列表,我在尝试检索时遇到了问题。

我在这个例子中知道我正在通过一个错误的方法获取:获取实体的字符串ID但这取决于重构;)

这是我的规范和带有连接的谓词循环:

public static Specification<Applicant> applicantsMatchMobility(final String... mobilities) {
    return new Specification<Applicant>() {

        @Override
        public Predicate toPredicate(Root<Applicant> root, CriteriaQuery<?> query, CriteriaBuilder builder) {

            Collection<Predicate> predicates = new ArrayList<>();

            Join<Applicant, Mobility> applicantMobilityJoin = root.join("mobility");

            for(String mobility : mobilities) {
                predicates.add(builder.equal(applicantMobilityJoin.<Mobility>get("id"), Integer.parseInt(mobility)));
            }

            return builder.and(predicates.toArray(new Predicate[predicates.size()]));

        }

    };
}

当我只传递一个Mobility实体的id但它是一个String ids数组&gt;时,它工作正常。 1这总是返回0 ..

这是我的junit测试:

 @Test
public void ShouldHaveOneApplicantWhenSearchMobilityMultipleTotallyEquals(){

    String mobilitiesId[] = {"0", "1"};
    List<Applicant> applicantList = applicantRepository.findAll(applicantsMatchMobility(mobilitiesId));
    Assert.assertNotNull(applicantList);
    Assert.assertEquals("Result", 1, applicantList.size());

    Applicant applicant = applicantList.get(0);
    Assert.assertNotNull(applicant);
    Assert.assertEquals("Result", "XBNC", applicant.getFirstName());

}

以下是生成的请求:

select
    * 
from
    applicant applicant0_ 
inner join
    applicant_mobility mobility1_ 
        on applicant0_.id=mobility1_.id_applicant 
inner join
    mobility mobility2_ 
        on mobility1_.id_mobility=mobility2_.id 
where
    mobility2_.id=0 
    and mobility2_.id=1

这是我的测试数据集:

    <?xml version='1.0' encoding='UTF-8'?>
<dataset>
    <MOBILITY ID="0" NAME="sud"/>
    <MOBILITY ID="1" NAME="nice"/>
    <MOBILITY ID="2" NAME="cannes"/>
    <MOBILITY ID="3" NAME="nowhere"/>

    <TAGS ID="0" NAME="tags1"/>
    <TAGS ID="1" NAME="tags2"/>
    <TAGS ID="2" NAME="tags3"/>

    <STATUS ID="0" NAME="status1"/>
    <STATUS ID="1" NAME="status2"/>

    <AVAILABILITY ID="0" NAME="availability1"/>

    <APPLICANT ID="0" LAST_NAME="XBNC" FIRST_NAME="XBNC" YEAR="2001" WAGE_CLAIM="50" ID_STATUS="0" ID_AVAILABILITY="0" />
    <APPLICANT ID="1" LAST_NAME="XBN" FIRST_NAME="XBN" YEAR="0" WAGE_CLAIM="70" ID_STATUS="1" ID_AVAILABILITY="0"/>
    <APPLICANT ID="2" LAST_NAME="MI" FIRST_NAME="MI" YEAR="1995" WAGE_CLAIM="0" ID_STATUS="1" ID_AVAILABILITY="0"/>
    <APPLICANT ID="3" LAST_NAME="bisTronomique" FIRST_NAME="bisTronomique" YEAR="-400" WAGE_CLAIM="0" ID_STATUS="0" ID_AVAILABILITY="0"/>

    <APPLICANT_MOBILITY ID_APPLICANT="0" ID_MOBILITY="0"/>
    <APPLICANT_MOBILITY ID_APPLICANT="0" ID_MOBILITY="1"/>

    <APPLICANT_MOBILITY ID_APPLICANT="1" ID_MOBILITY="0"/>
    <APPLICANT_MOBILITY ID_APPLICANT="1" ID_MOBILITY="2"/>

    <APPLICANT_MOBILITY ID_APPLICANT="2" ID_MOBILITY="1"/>

    <APPLICANT_TAGS ID_APPLICANT="0" ID_TAGS="0"/>
    <APPLICANT_TAGS ID_APPLICANT="0" ID_TAGS="1"/>
    <APPLICANT_TAGS ID_APPLICANT="0" ID_TAGS="2"/>

</dataset>

您是否可以在此测试中看到我只需要回到&#34; XBNC&#34;申请人因为他包含2个相应的MobilityeID ..

2 个答案:

答案 0 :(得分:1)

这是一个基本的SQL逻辑问题,我猜你正在尝试检索对应于mobiliy ID 0和1的2行:当前代码中的where子句(critria)构建[where id ='0'< b>和 id ='1'],由于特定的表字段不能同时等于两个值,因此总是不返回任何行。
而不是and(),您需要使用or()方法,它将构建一个where子句,如[where id ='0' id ='1']。只提供一行和一行只将mobilityID设置为1,而一行和一行只将mobilityID设置为0,那么您应该从运行查询中获得两行。

更新
在您的问题编辑后,我想我可以看到您的问题所在。这仍然是一个SQL问题,但这意味着您的查询需要稍微复杂一些。您需要获得如下查询:

select * from applicant applicant0_ inner join applicant_mobility mobility1_ on applicant0_.id=mobility1_.id_applicant inner join mobility mobility2_ on mobility1_.id_mobility=mobility2_.id where exists ( select * from applicant_mobility where id_applicant = applicant0_.id and id_mobility = 1; ) and exists ( select * from applicant_mobility where id_applicant = applicant0_.id and id_mobility = 0; )
请注意子查询中applicantapplicant_mobility之间的联接。这实际上是这样说的:对于主要查询找到的每个申请者实例,验证它是否在至少有一个移动性设置为0的相应applicant_mobility实例的集合中,以及另一个移动性设置为1的实例。 因此,您的函数中的循环需要为每个提供的移动设备ID创建子查询,然后使用构建器and()exists()将它们组合在一起。

答案 1 :(得分:0)

添加@NotSoOldNick指定的内容。您似乎需要的是一个IN子句,只能检索匹配的移动性匹配的申请人。您可以将其更改为以下或稍微类似。

public static Specification<Applicant> applicantsMatchMobility(final String... mobilities) {
    return new Specification<Applicant>() {

        @Override
        public Predicate toPredicate(Root<Applicant> root, CriteriaQuery<?> query, CriteriaBuilder builder) {

            Collection<Predicate> predicates = new ArrayList<>();

            Join<Applicant, Mobility> applicantMobilityJoin = root.join("mobility");

            Expression<String> idExp = applicantMobilityJoin.<Mobility>get("id");

            return builder.and(idExp.in(mobilities));

        }

    };
}