【问题标题】:JPQL TREAT AS /LEFT OUTER JOINJPQL TREAT AS /LEFT OUTER JOIN
【发布时间】:2015-05-19 23:43:43
【问题描述】:

我正在尝试 JPA 2.1 (eclipselink) 中的“TREAT AS”功能,但遇到了来自 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 表之间有一个左连接。 这会导致每个目标用户的帐户类型检索一行。

这是生成的 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');

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

谢谢你帮助我!

【问题讨论】:

  • 尝试简化查询,然后发布 SQL(不需要所有连接来证明您的问题)。 Treat 应该是强制子类表之间的连接和/或过滤类型字段,所以我不太清楚你的期望。
  • 当然,我添加了sql查询并简化了jpql。
  • 刚刚意识到您可能期望使用左连接返回空值,而您的 SQL 在 t7 和 t6 之间有一个内部连接(我假设的目标和帐户)。

标签: java jakarta-ee jpa jpql


【解决方案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 值)。

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

经过反复思考,我有了一个想法:

    "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

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

【讨论】:

    【解决方案2】:

    TREAT 表达式是一种允许在查询中访问子类参数的方式;过滤是必要的副产品,但不是主要意图。它更符合您可能想要查询“薪水> 100k的工人或直接数量

    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。

    【讨论】:

    • 不幸的是,我还需要过滤 ADAccount 类的属性,但我想我也可以在 where 子句中使用 TREAT。我会试试的。
    • 我试过了,JPA 加入了所有 AbstractAccount 子类表(还有很多表 T.T)。我必须让 TREAT 函数工作......
    • 如果您希望您的第一个查询能够工作,但发现 null target.accounts 正在被过滤,这是预期的 - 规范在 4.4.9 中声明:“也就是说,在连接的情况下,被引用的对象不参与结果,并且在限制的情况下,关联的谓词为假”。在这种情况下,TREAT(target.accounts AS ADAccount) 将强制对 target.accounts 进行内部连接。这就是我认为规范在大多数示例的 where 子句中使用 Treat 的原因。
    • 你能给我规格的链接吗?
    猜你喜欢
    • 1970-01-01
    • 2014-08-10
    • 2013-03-10
    • 2010-09-29
    • 2018-12-04
    • 2015-04-02
    • 1970-01-01
    • 2014-03-16
    相关资源
    最近更新 更多