JPQL TREAT AS / LEFT OUTER JOIN

时间:2015-03-17 13:54:38

标签: java java-ee jpa jpql

我正在尝试" TREAT AS"来自JPA 2.1(eclipselink)的功能,我遇到了JPA的错误:

异常说明:ReportQuery结果大小不匹配。期待[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))。 用户有一个AbstractAccount列表。

我想使用userTarget的AD帐户选择工作表。如果没有userTarget或者userTarget没有AD帐户(左连接),我想要null。

问题来自对待操作员。 SQL生成的查询在AbstractAccount表和ADAccount表之间具有左连接。 这会导致每个帐户类型的targetUser检索一行。

这是生成的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之间看到左外连接。 此外,select子句中不存在ad_account表。 (idApp字段是主键的一部分,并保持唯一(userId,idApp)约束)。 我不知道我的理解或JPA是否有问题。

谢谢你的帮助!

2 个答案:

答案 0 :(得分:1)

 "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";

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

如果目标有一个AD帐户和至少一个其他帐户类型,我还有一个工作表的多行:一行设置了AD帐户属性,另一行设置了空值(这些不同的值阻止了distinct子句,所以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

它工作正常,但非常难看。我有一天会跳过另一种方法。

答案 1 :(得分:0)

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。