JPA/JPQL加入Subselect/Subquery

JPA/JPQL JOIN Subselect/Subquery

我无法将一些简单的 SQL 语句转换为 JPQL,因为使用了 JPQL 不支持的子查询。

谁能给我一个提示,如何使用 JPQL 或 JPA2 标准获得相同的结果 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"

试试这个:

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

首先,根据 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 phone 号码 - 因此每个 phone 号码都会有一行一个人,即使 he/she 有多个 phone 号码不是手机号码。

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

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'