JPA Query Select many-to-one Query with a count

gordon picture gordon · Apr 2, 2013 · Viewed 8.8k times · Source

So I'm trying to write a JPA query based on the classes below (dumbed down a bit) that will produce the following:

So I have two objects: Thing and Person. A Person can hold a reference to a single Thing. Here are simplified version of the Classes:

public class Thing {
    @Id
    public Long id;
    public String name;
    public String description;
}

public class Person {
    @Id
    public Long id;
    public String firstname;
    public String lastname;
    @ManyToOne
    public Thing thing;
}

I'm trying to write a JPA query that will give me all the details of every Thing object as well as the number of times that Thing object is referenced by a Person object. Note that a Person could have the value null for Thing. Also a Thing object might not be referenced by any Person object at all but should still be listed.

So given the following tables:

Thing Table
| id | name | description |
|  1 | thg1 | a thing     |
|  2 | thg2 | another one |
|  3 | thg3 | one more    |

Person Table
| id | firstname | lastname | thing |
|  1 | John      | Smith    |     1 |
|  2 | Simon     | Doe      |     3 |
|  3 | Anne      | Simmons  |     1 |
|  4 | Jessie    | Smith    |     1 |
|  5 | Adam      | Doe      |     3 |
|  6 | Phil      | Murray   |  null |

I would end up with a result like:

| id | name | description | amount |
|  1 | thg1 | a thing     |      3 |
|  2 | thg2 | another one |      2 |
|  3 | thg3 | one more    |      0 |

How would I go about writing that JPA Query? (If it makes a difference I'm using the Play Framework 1.2.5)

Answer

dcernahoschi picture dcernahoschi · Apr 2, 2013

It should be something like this:

select t.id, t.name, t.description, count(p) as amount
         from Person as p right join p.thing as t group by t.id

The reason for the unusual "right join" is that JPA queries requires mappings between query classes and you only have one from Person to Thing.

If you had a mapping from Thing to Person:

class Thing {
    ...
    @OneToMany
    Set<Person> persons;
}

you could use the classic "left join":

select t.id, t.name, t.description, count(p) as amount
             from Thing as t left join t.persons as p group by t.id