5

This may seem basic but it's late and I'm having trouble with the following.

class Group {

  @Id
  String id;
}

class Participation {

  @Id
  String id;

  @ManyToOne
  @JoinColumn(name = "GROUP_ID")
  Group group;

  @ManyToOne
  @JoinColumn(name = "USER_ID")
  User user;
}

class User {

  @Id
  String id;

  @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
  Set<Participation> participations;
}

Class diagram

So

Participation -->1 Group

and User 1<-->N Participation

How can I retrieve all Groups with, for a given User, the associated Participation (or null is there is none)? I've been playing with join fetches but to no avail so far...

Many thanks,

CN

PS. I can do this in SQL thus :

select g.d, p.id 
from group as g 
left join participation as p 
on p.group_id = g.id and p.user_id = 2;
Defenestrate
  • 433
  • 2
  • 6
  • 14

2 Answers2

7

(Probably some typo in the HQL itself but the idea should be correct)

What you are asking, based on your SQL and description, is find out all Participation (and its corresponding Group) based on User, which is simply

select p.id, p.group.id from Participation p where p.user.id = :userId

To make it better, you should fetch the entities instead:

from Participation p left join fetch p.group where p.user.id = :userId

There were some confusions in understanding what you were trying to do: You want all groups (regardless of condition). And, for a given user, you want to find all groups and participations that user is involved.

Though it should be possible using Right-outer-join:

select g, p from Participation p 
    right outer join p.group g 
    where p.user.id=:userId

Or, in later version of Hibernate (>= 5.1 ?), it allow explicit join (haven't tried before, you may give it a try) (Replace with with on if you are using JPQL):

select g, p from Group g
    left outer join Participation p 
        with p.group = g
    left outer join p.user u
    where u.id = :userId

Or you may use other techniques such as subquery etc. However I would rather separate them into simpler queries in your case and do a simple aggregation in your code.

The basic idea is to have

  1. Query for all groups: from Groups
  2. Query for all participations for a user: from Participation p join fetch p.group where p.user.id=:userId

Then you can aggregate them easily to the form you want, e.g. Map<Group, List<Participation>>, or even more meaningful value object.

The benefit is the data access query is simpler and more reusable (esp if you are wrapping them in DAO/Repository finder method). One more round trip to DB shouldn't cause any obvious performance impact here.

Adrian Shum
  • 38,812
  • 10
  • 83
  • 131
  • 2
    Thanks, except that this way I don't get the Groups without a Participation for the specified User. – Defenestrate Oct 04 '16 at 16:01
  • I don't have dbms on hand but doing a left join as your example with user id should still only give you group with participant isn't it? And you asked for "for a given user, the given participation" which giving you participation with no user is contradict with what you asked for. Anyway, have a try for `from Participation p left join p.user u where u.id =:userid or u is null` – Adrian Shum Oct 04 '16 at 16:12
  • Agh I guess I misunderstood what you asked. You actually want all groups right? Anyway iirc, HQL do support Right Join, so you can start with Participation right join p.group? – Adrian Shum Oct 04 '16 at 16:16
-1

You need to map the participation relationship in the Group entity. If the relationship between Participation and Group is 1..N:

class Group {
  String id
  List<Participation> participations
}

The JPQL can be:

SELECT g.id, p.id FROM Group g
JOIN g.participations p
JOIN p.user user
WHERE user.id = :idUser

You can receive this information as List<Object[]> (Object[0] is group id and Object[1] is participation id) or use SELECT NEW.

Without map the Group/Participation relationship, you can do:

SELECT g.id, p.id FROM Group g, Participation p
JOIN p.user user
JOIN p.group groupp
WHERE g.id = groupp.id
AND user.id = :idUser

But you can't do a LEFT JOIN using this strategy. The query above behaviours like a JOIN.

It's normal with Hibernate you map the side of a relationship that you would like to make a query. So, I recommend the first approach, mapping the relationship.

Community
  • 1
  • 1
Dherik
  • 17,757
  • 11
  • 115
  • 164
  • I'm afraid the relationship between Participation and Group isn't 1:1 (I've added a diagram to the original question). – Defenestrate Oct 01 '16 at 08:08
  • @ChambreNoire, no problem! I edited my question, but works in the same way – Dherik Oct 04 '16 at 02:23
  • No way of doing it without adding the Group>Participation association? – Defenestrate Oct 04 '16 at 16:00
  • Theta join is something I would rather avoid (esp new version of hibernate allow arbitrary "on" clause for joins now). And the relationship in entities should be good enough to write meaningful HQL – Adrian Shum Oct 04 '16 at 16:10
  • @AdrianShum, What join are you talking? He not provide the version of Hibernate too. – Dherik Oct 04 '16 at 16:13
  • Theta style join means : `FROM Group g, Participation p ... WHERE g.id = groupp.id ...` . There are drawbacks using this: 1. it simply cannot do what OP is asking for (outer joins) 2. It is difficult to read. – Adrian Shum Oct 05 '16 at 07:12