JPQL 视为 /左外连接



我正在尝试JPA 2.1(eclipselink)中的"TREAT AS"函数,但我遇到了JPA的错误:

异常说明:报表查询结果大小不匹配。 期待 [263],但检索到 [197]

这是我的 JPQL 查询(我更改了一些部分以使其更明确):

String jpql = "select distinct s, accountAD "
            + "from SheetAccountUser s "
            + "left join fetch s.userTarget target "
            + "left join TREAT(target.accounts AS ADAccount) accountAD ";

ADAccount 是 AbstractAccount (@Inheritance(strategy = InheritanceType.JOINED) 中的一个子类。用户有一个抽象帐户列表。

我想选择具有用户目标的 AD 帐户的工作表。如果没有用户目标或用户目标没有 AD 帐户(左联接),我想要空。

问题来自治疗操作员。SQL 生成的查询在 AbstractAccount 表和 ADAccount 表之间有一个左联接。这会导致检索目标用户的每个帐户类型的一行。

下面是生成的 SQL 查询:

SELECT DISTINCT 
t0.Id, --etc
t6.Id, t6.name, --etc
t7.userId --etc
FROM sheet t0 
LEFT OUTER JOIN user t6 ON (t6.Id = t0.userTargetId),
account t7 LEFT OUTER JOIN ad_account t8 ON ((t8.userId = t7.userId) AND (t8.idApp = t7.idApp))
WHERE (t7.userId = t6.Id) AND (t7.DTYPE = 'ADAccount');

我们可以看到帐户和ad_account之间的左外部连接。此外,ad_account表在 select 子句中不存在。(idApp 字段是主键的一部分,并维护唯一的(userId、idApp)约束)。我不知道是我的理解还是JPA的问题。

谢谢你帮助我!

 "select distinct s, accountAD "
                + "from SheetAccountUser s "
                + "left join fetch s.userTarget target "
                + "left join target.accounts accountAD where TYPE(accountAD) = ADAccount";

此请求不会返回具有没有 AD 帐户的目标的工作表。

 "select distinct s, accountAD "
                + "from SheetAccountUser s "
                + "left join fetch s.userTarget target "
                + "left join target.accounts accounts "
                + " join TREAT(accounts AS ADAccount) accountAD";

这个生成的 SQL 与我的第一个 jpql 请求相同,并生成一个 Eclipselink 错误。

如果目标有一个 AD 帐户和至少一种其他帐户类型,则一张工作表也有多行:一行设置了 AD 帐户属性,另一行设置了空值(这些不同的值阻止了不同的子句,DTYPE 值也是如此)。

幸运的是,我只需要 2 个关于 AD 帐户的信息:它的存在和一个布尔值"停用"。

经过更多的反思,我有一个想法:

    "select u, "
                // 0 if line with no target or no ADAccount or with another account type, else 1 (one 1 by sheet/target) 
                + "sum( "
                + "     case "
                + "     when accountAD.desactivated is not null then 1 "
                + "     else 0 "
                + "     end "
                + ") as ADAccountExists, "
                // the target have an AD Account desactivated
                + "sum( " 
                + "     case "
                + "     when compteAD.desactivated = 1 then 1 " 
                + "     else 0 "
                + "     end" 
                + ") as ADAccountDesactivated " 
                + "from SheetAccountUser s "
                + "left join fetch s.userTarget target "
                + "left join treat(target.accounts as ADAccount) accountAD "
                + "group by s,target " //the group by maintains unicity of the sheets

它工作正常,但非常丑陋。我希望有一天能找到另一种方法。

TREAT 表达式旨在允许在查询中访问子类的参数;过滤是必要的副产品,但不是主要意图。 它更像是您可能想要查询"薪水> 100k 的工人或直接数量为 <10 的经理人员"。 在这种情况下,人员+员工和人员+经理之间的严格连接会阻碍查询。

TYPE 表达式允许您自己控制过滤,以便获得所需的严格结果。 像这样:

"select distinct s, accountAD "
            + "from SheetAccountUser s "
            + "left join fetch s.userTarget target "
            + "left join target.accounts accountAD where TYPE(accountAD) = ADAccount";

可能更需要。 请记住,您需要明确列出要包含的任何子类。

如果必须使用 Treat 进行筛选,但希望通过 target.accounts 进行外部联接,请尝试以下操作:

"select distinct s, accountAD "
                + "from SheetAccountUser s "
                + "left join fetch s.userTarget target "
                + "left join target.accounts accounts "
                + " join TREAT(accounts AS ADAccount) accountAD";

可能会起作用,但您可能只想在需要时在 where 子句中使用 TREAT。

相关内容

最新更新