【问题标题】:JPA/JPQL JOIN Subselect/SubqueryJPA/JPQL JOIN 子选择/子查询
【发布时间】:2015-10-02 07:12:42
【问题描述】:

由于使用了 JPQL 不支持的子查询,我无法将一些简单的 SQL 语句翻译成 JPQL。

谁能给我一个提示,如何使用 JPQL 或 JPA2 Criteria API 实现相同的结果?

给定(简化的假数据来证明问题):

CREATE TABLE person (id integer, name text);
CREATE TABLE phone (id integer, person_id integer, type text, number text);

INSERT INTO person VALUES (1, "John");
INSERT INTO person VALUES (2, "Mike");
INSERT INTO person VALUES (3, "Paul");
INSERT INTO person VALUES (4, "Walter");

INSERT INTO phone VALUES (1, 1, "MOBILE", "+49-123-11111");
INSERT INTO phone VALUES (2, 1, "HOME"  , "+49-123-22222");
INSERT INTO phone VALUES (3, 2, "WORK"  , "+49-123-33333");
INSERT INTO phone VALUES (4, 4, "MOBILE", "+49-123-44444");

-- Select all from person and their mobile number if possible
-- This query has to be translated to JPQL

SELECT person.name, mobile.number FROM person LEFT JOIN (
  SELECT * FROM phone WHERE type = "MOBILE"
) AS mobile ON person.id = mobile.person_id;

预期结果:

| name   | number        |
|--------|---------------|
| John   | +49-123-11111 |
| Mike   |               |
| Paul   |               |
| Walter | +49-123-44444 |

Java:

class Person {
    String name;
    List<Phone> phones;
}

class Phone {
    String type;
    String number;
}

JPQL(未按预期工作:-():

SELECT person.name, phone.number FROM Person person
    LEFT JOIN person.phones AS phone
    WHERE phone.type = "MOBILE"

【问题讨论】:

    标签: java jpa join subquery jpql


    【解决方案1】:
    SELECT person.name, phone.number 
       FROM Person AS person LEFT JOIN person.phones AS phone 
       ON phone.type = 'MOBILE'
    

    您还可以将ON 关键字替换为特定于休眠的WITH

    SELECT person.name, phone.number 
       FROM Person AS person LEFT JOIN person.phones AS phone 
       WITH phone.type = 'MOBILE'
    

    【讨论】:

      【解决方案2】:

      首先,根据Java Persistence Wikibook,一些 JPA 提供程序(如 EclipseLinkTopLink)支持 FROM 子句中的子选择 - 尽管这在JPA 规范。

      在 JPA 2.1 中,您可以使用 LEFT JOINON

      SELECT person.name, phone.number
      FROM Person person
      LEFT JOIN person.phones AS phone ON phone.person = person AND phone.type = 'MOBILE'
      

      在 JPA 2.1 之前,您可以使用 case 表达式:

      SELECT person.name, 
        CASE WHEN (phone.type = 'MOBILE') THEN phone.number ELSE '' END 
      FROM Person person
      LEFT JOIN person.phones AS phone
      

      但这只会擦除所有 none-mobile 电话号码 - 因此一个人的每个电话号码都会有一行,即使他/她有多个电话号码不是一个手机号码。

      如果您的 JPA 提供程序正确呈现这些(Hibernate 在大多数情况下),您可以使用数据库的 列表聚合 功能(如 Oracle 中的 LISTAGG)。这对于前两个选项也是有意义的 - 如果一个人可以拥有多个手机号码。

      【讨论】:

        【解决方案3】:

        试试这个:

           SELECT person.name, phone.number 
             FROM Person person
        LEFT JOIN person.phones AS phone
            WHERE phone.type IS NULL OR phone.type = "MOBILE"
        

        【讨论】:

        • 这也行不通。 WHERE 子句在加入电话表后删除行。因此,只有 WORK 号码的 Mike 将被删除。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-11-13
        • 2015-05-26
        • 2015-08-05
        • 1970-01-01
        • 2019-09-19
        • 1970-01-01
        • 2011-07-05
        相关资源
        最近更新 更多