简体   繁体   中英

HQL left join with condition

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;

(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, eg 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.

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.

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