-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 
Author Message
 Post subject: Filter collection elements
PostPosted: Wed Feb 25, 2009 1:51 am 
Newbie

Joined: Mon May 14, 2007 1:57 am
Posts: 12
Hibernate version: Hibernate 3.2.6ga
Name and version of the database you are using: MySQL

Hi,
I have the following relationships:
Code:
1) Client    1-M    Entity    (a Client has many Entities, an Entity belongs to a Client, bi-directional)
2) Entity    1-1    User      (an Entity can have a User, a User belongs to an Entity, bi-directional.)
3) User      M-M    Rol       (a User has many Roles, a Rol can be assigned to many users, unidirectional to the right)

So, an Object would be something like this:
Code:
client
   - entities (NOTE: an entity can have a user, it's optional)
         - user
               - roles (ROLE1, ROLE2, or ROLE3)

* There are clients that have entities, and there are some clients that don't have any entity.
* Some clients that have entities have a user assigned to it, and there're some entities that don't have assigned a user.

I need to request an specific client. Also I need to fetch their entities that have assigned a user with an specific role. For example:

Request Client #22, and all entities with users assigned to ROLE1.

Case A) If the client requested doesn't have any entities with the requested role, then it must return a Client object with an empty collection of entities.
Case B) If the client requested does have entities (some may have assigned a user with the requested role and some others with a role different from the requested), then it must return a Clien object with the collection of entities that have the role requested.


The Query I have right now is the following:

Code:
Query q = getSession()
   .createQuery("select c from Client as c " +
      "left join fetch c.entities as e " +
      "left join e.user.roles as r " +
      "where c.id=:cid and r.id IN ('ROLE1') " +
      "order by e.name asc)")
   .setParameter("cid", id);


It works well for case B... For case A, it returns a null client object.

I also tried filtering the collection in User mapping file:
Code:
... User.hbm.xml file
<bag name="roles" table="USER_ROL">
   <key column="USER_ID"/>
   <many-to-many class="Rol" column="ROL_ID"/>
   <filter name="myFilter" condition=":myRol = ROL_ID"/>
</bag>
...

This filter returns all entities but don't return Roles that are different from the requested rol. So, it doesn't work for me.

Does anyone have any idea about how to filter a collection given this scenario?
Thanks in advance :)


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 25, 2009 3:26 am 
Expert
Expert

Joined: Wed Mar 03, 2004 6:35 am
Posts: 1240
Location: Lund, Sweden
Quote:
It works well for case B... For case A, it returns a null client object.


This happens because the condition on the role is part of the where clause. As you said... in case A the client doesn't have any user with the specified role, so the where condition doesn't match any row. To make it match you have to move the condition on the role to make it part of the left join. Try something like:

Code:
...
left join e.user.roles as r with r.id IN ('ROLE1')
where c.id=:cid
...


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 25, 2009 1:35 pm 
Newbie

Joined: Mon May 14, 2007 1:57 am
Posts: 12
I already tried what you propose:
Code:
.createQuery("select c from Client as c " +
   "left join fetch c.entities as e " +
   "left join e.user.roles as r with r.id IN('DISTRIBUIDOR') " +
   "where c.id=:cid")
.setParameter("cid", id);

but i get the following exception:
Code:
with-clause expressions did not reference from-clause element to wich the with-clause was associated [here goes the query]
org.hibernate.hql.ast.InvalidWithClauseException
at org.hibernate.hql.ast.HqlSqlWalker.handleWithFragment(HqlSqlWalker.java)
....

So I modify my query as follows (separate e.user.roles into two joins):
Code:
.createQuery("select c from Client as c " +
   "left join fetch c.entities as e " +
   "left join e.user as u left join u.roles as r with r.id IN('ROLE1') " +
   "where c.id=:cid")
.setParameter("cid", id);

... and it worked for case A (now, it returns clients that doesn't have any enitities yeah!), but it doesn't work for case B (now, it returns all entities even those with a role different from "ROLE1" :S) I don't know why!

A work around I've come up is:
Code:
//First retrieve just the client:
Query q1 = getSession()
   .createQuery("select c from Client as c " +
      "where e.cliente.id=:cid")
   .setParameter("cid", id);
c = (Client) q1.uniqueResult();

//Then retrieve the entities of that client who have a user assigned with ROL1:
Query q2 = getSession()
   .createQuery("select e from Entidad as e " +
      "left join e.usuario.roles as r " +
      "where e.cliente.id=:cid and r.id IN ('DISTRIBUIDOR')")
   .setParameter("cid", id);

//Then set those entities to client object:
c.setEntities(q2.list());


This works for me, but I think there should be a better way to do this, with only one query... I feel my solution a little dirty.
Any suggestions?


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 25, 2009 3:22 pm 
Expert
Expert

Joined: Wed Mar 03, 2004 6:35 am
Posts: 1240
Location: Lund, Sweden
I guess I misunderstood what you wanted. The problem here is that you need to left join two collections (first Client.entities, and then User.roles) and you want to put a restriction on the first collection based on what is in the second. Hmm... it may be hard to do with just joins... I can't come up with something right now...

But the second query from the working example in your last post can probably be used as a subquery in the first query. Something like:

Code:
select c from Client as c
left join fetch c.entities as e with e.id in (
  select e.id from Entidad as e
  left join e.usario.roles as r
  where r.id in ('DISTRIBUIDOR')
)
where c.id=:cid


I am not 100% sure the above will work. It is kind of pushing the limit of what I can come up with without actually trying it out.

Another possibility is maybe to use the collection filter feature. It would be rather similar to the example in your last post, but it may feel a bit "cleaner". See http://www.hibernate.org/hib_docs/v3/re ... -filtering for more information about collection filters.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.