JPA根据集合元素匹配或空集合选择

时间:2011-05-18 05:22:55

标签: java jpa jpa-2.0 jpql

我有一个Unit,它有一个UnitType和一个Organization。我有一个合同,其中包含UnitTypes和Organization的集合。我想选择单位,以及在其集合中具有Unit的UnitType的合同,或者如果没有匹配则选择具有空UnitType集合的UnitType。

为了澄清,在每种情况下我都想选择单位。如果在合约的UnitTypes集合中存在具有单位指定类型的合约,那么我想选择该合约。如果这样的合同不存在,那么我想选择根本没有UnitTypes的合同。换句话说,我希望合同适用于此单位的类型,但如果它不存在,我将采用单位类型不可知的合同作为默认值。

示例1:

Unit A has type XYZ.
Contract B has types [ ABC, DEF ]
Contract C has types []

在这种情况下,我会选择单位和合同C,因为B在类型上没有匹配。

示例2:

Unit A has type XYZ
Contract B has types [XYZ, ABC]
Contract C has types []

在这种情况下,我会选择合约B,因为它与单位的类型相匹配。

以下查询适用于示例2,但不适用于示例1。

SELECT NEW mypackage.view.MyAggregateView( 
    u
    , MAX(sc.serviceDate)
    , c.survey.key )
FROM Contract c
    , Unit u 
    LEFT JOIN u.serviceCalls sc 
    WHERE c.organization.key = u.organization.key 
    AND u.organization.key = :organizationKey 
    AND ((u.unitType MEMBER OF c.unitTypes) 
        OR (c.unitTypes IS EMPTY))
    GROUP BY u, c.survey.key

如何在这两种情况下完成这项工作并确保获得正确的合同?

又一个例子:

我最近再遇到这个问题。我有一个区域,其中包含一系列邮政编码和可选的组织集合。我还有一个单位,它有一个1-1到一个组织,并有一个邮政编码。我想获得该地区邮政编码中的所有相应单位。如果该地区没有组织,那么我应该获得邮政编码中的所有单位,否则我应该只获得组织与该地区内指定的组织之一相匹配的单位。

这是我的查询

Select u.key, u.organization.key
from Unit u, Region r
where r.key = -1
and u.address.postalCode member of r.zips
and r.organizations is empty

此查询可以获得我所有预期的结果。以下查询不应该限制结果集,因为它只添加了一个OR,但没有给出任何结果。

Select u.key, u.organization.key
from Unit u, Region r
where r.key = -1
and u.address.postalCode member of r.zips
and ((r.organizations is empty) OR (r.organizations is not empty and u.organization member of r.organizations))

我正在使用eclipse link 2.0.1对抗postgres 9.我也使用eclipselink 2.2.0得到了相同的结果。

1 个答案:

答案 0 :(得分:1)

您的主要问题是“来自Unit u,Contract c ...其中c.organization.key = u.organization.key”是Unit和Contract之间的内部联接。根据定义,如果没有匹配的合同,这将永远不会返回结果。在“或c.unitTypes为空”之前,行从结果集中删除了一半的条件甚至有机会触发。

还有一个更微妙的问题是,如果您可能有多个引用相同单元类型的合同,则可以在查询中返回重复单元。但是,这种联合可能是不可避免的,因为你试图获得合同和单位,而不仅仅是单位。 (否则你可以使用exists / not exists而不是join。)

现在,听起来你真的不能用一个连接来做你想要的逻辑。有条件的逻辑,比如“拿东西,如果它存在,别拿东西,但不是两个”需要多个连接,然后是case / when逻辑选择使用哪个。

此时我想知道你是否会因为两个单独的查询而变得更好。根据常见情况和应用程序的整体架构,您的性能甚至可能更好地运行两个简单的查询并进行两次往返,而不是进行复杂的查询以避免往返。即使你的性能受到轻微打击,我也几乎更喜欢它的可读性和可维护性。

也就是说,如果我必须在SQL中执行此操作,并且它绝对必须在单个查询中,那么它将是可行的但完全不是:

  select u.*, c.* from (
    select unit_id, 
         coalesce(matching.contract_id, non_matching.contract_id) 
         as contract_id
      from Unit u
        **left** join Contract matching on ([match on unit type]) 
        left join Contract non_matching on ([unit types empty])
   ) subquery join Unit u on subquery.unit_id = u.unit_id 
     join Contract c on subquery.contract_id = c.contract_id

或者将两个查询与UNION ALL组合在一起,并在返回第一个结果后停止。

然而,这两个选项都没有转换为JPA / JPQL。 JPQL没有UNION运算符,并且JPQL不支持任意条件的外连接,仅在导航关系时支持“left join u.serviceCalls”。我不认为你可以将笛卡尔“从契约c,单位u”变成外连接。

因此,对于JPQL,我很伤心地说有没有什么好办法让你在一个单一的查询想要的东西。