Bug 239 : QUERY LANGUAGE CHANGE: change predicates on *ToMany to use separate join from fetch
Priority 
High
Reported Version 
 
Logged By 
Rob
Status 
Fixed
Fixed Version 
2.5.0
Assigned To 
 
Product 
Ebean - core
Duplicate Of 
 
Created 
10/03/2010
Updated 
10/03/2010
Type 
Bug
 
Attachments 
No attachments

From Daryl:

User user = new User();
user.setName("user");
Ebean.save(user);

Role role1 = new Role();
role1.setName("role1");
Ebean.save(role1);
user.getValidRoles().add(role1);
Role role2 = new Role();
role2.setName("role2");
Ebean.save(role2);
user.getValidRoles().add(role2);
Ebean.save(user);

List<Integer> roleIds = new ArrayList<Integer>();
roleIds.add(role1.getId());
List<User> result =
Ebean.find(User.class).join("validRoles").where().in("validRoles.id",
roleIds).findList();
assertEquals(1, result.size());
User resultUser = result.get(0);
assertEquals(user, resultUser);
assertEquals(2, resultUser.getValidRoles().size());

It returns only the valid roles that are in the roleIds list. If I
remove join("validRoles") from the query, the validRoles collection is
lazy loaded (both roles) and the test passes. Surely this difference
indicates a bug? JPA would always return the entire list of
validRoles.

 
Rob 10 Mar 05:14
Long discussion

There is a long discussion on the google group about this topic:

http://groups.google.com/group/ebean/browse_thread/thread/f5b57df7e52ace7e

This is a 30 post discussion so I'm not going to repeat it all here.

The end result is that there are 2 interpretations of a where clause predicate on a 'many' property. 1 is the JPQL interpretation which means that the predicate is used to filter the top level objects. The 2nd interpretation current with Ebean is that it is a filter than can be applied to the fetch of the many itself.

In short we are changing Ebean to follow the JPQL interpretation, but looking to add "filter Many" type functionality. This is occasionally required when the *ToMany relationship has a high cardinality. For example, customers with 10,000's of orders - you may want to fetch customers with their orders.. but filter on the orders only fetching the new orders made in the last week.

Rob 10 Mar 06:04
Example
List<MUser> list = Ebean.find(MUser.class)
    .join("roles")
    // the where on a 'many' (like orders) requires an 
    // additional join and distinct which is independent
    // of a fetch join (if there is a fetch join) 
    .where().eq("roles.roleName", "role2special")
    .findList();

... produces ...

<sql summary='MUser +many:roles' >
select distinct m.userid c0, m.user_name c1
        , mr.roleid c2, mr.role_name c3 
from muser m
left outer join mrole_muser mrz_ on mrz_.muser_userid = m.userid 
left outer join mrole mr on mr.roleid = mrz_.mrole_roleid 
join mrole_muser xrz_ on xrz_.muser_userid = m.userid 
join mrole xr on xr.roleid = xrz_.mrole_roleid  
where xr.role_name = ?  
order by m.userid
</sql>

Rob 10 Mar 06:06
Extra joins to support the where on the many...

Note that the table alias' starting with "X" ... are the extra joins required to support the "roles.roleName" = "role2special" predicate.

Rob 10 Mar 07:35
Simple example
Ebean.find(Customer.class)
    .select("id,status")
    .where().eq("orders.status", Order.Status.NEW)
    .findList();

produces ...

<sql summary='Customer' >
select distinct c.id c0, c.status c1 
from o_customer c
join o_order xo on xo.kcustomer_id = c.id  
where xo.status = ? 
</sql>

Rob 10 Mar 07:39
Another example

Adding the fetch join .. you now see 2 joins to o_order

Ebean.find(Customer.class)
    .select("id,status")
    .join("orders")
    .where().eq("orders.status", Order.Status.NEW)
    .findList();

produces ...

<sql summary='Customer +many:orders' >
select distinct c.id c0, c.status c1
        , co.id c2, co.status c3, co.order_date c4, co.ship_date c5, coc.name c6, co.cretime c7, co.updtime c8, co.kcustomer_id c9 
from o_customer c
left outer join o_order co on co.kcustomer_id = c.id 
left outer join o_customer coc on coc.id = co.kcustomer_id 
join o_order xo on xo.kcustomer_id = c.id  
where co.order_date is not null and xo.status = ?  
order by c.id
</sql>

Rob 10 Mar 07:40
Fixed in HEAD

Fixed in HEAD.

woResponse

Upload a file