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 提供程序,如 EclipseLink 和 TopLink 支持子选择 FROM
子句 - 虽然这在 JPA 规范中没有定义。
在 JPA 2.1 中,您可以将 LEFT JOIN
与 ON
一起使用:
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'