简体   繁体   中英

JPA/JPQL JOIN Subselect/Subquery

I have trouble to translate some easy SQL statement to JPQL, because of a used subquery, which is not supported by JPQL.

Can someone give me a hint, how to achieve the same result with JPQL or JPA2 Criteria API?

Given (Simplified fake data to demonstrate the problem):

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;

Expected result:

| 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 (not working as expected :-( ):

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 AS person LEFT JOIN person.phones AS phone 
   ON phone.type = 'MOBILE'

You can also replace the ON keyword with the hibernate specific WITH :

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

Try this:

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

First of all according to the Java Persistence Wikibook some JPA providers like EclipseLink and TopLink support sub selects in the FROM clause - although this is not defined in the JPA spec.

In JPA 2.1 you could use LEFT JOIN with ON :

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

Before JPA 2.1 you could use a case expression:

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

But this will just wipe all none-mobile phone numbers - so there will be a row for each phone number of a person, even if he/she has more than one phone number that is not a mobile number.

You could use the list aggregation function of your database (like LISTAGG in Oracle), if your JPA provider renders these correctly (Hibernate does in most circumstances). This would make sense for the first two options as well - if a person can have more than one mobile number.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM