Search code examples
mysqlhibernategrailshql

How to write HQL using LEFT OUTER JOINs on multiple hasMany associations that doesn't expect all JOINs to be non-empty


In my Grails application I had defined a domain object called Order (mapped to a work_order table since order is a reserved word in MySQL) which has a one-to-many relationship with another domain object called OrderProductItem in a property called products and also a one-to-many relationship with another domain object called OrderServiceItem in a property called services. In turn, OrderProductItem holds a StoreProduct domain object in a property called item and OrderServiceItem holds a StoreSvc domain object in a property called item.

In a query for Order LIST, I would like to support keywords search on the OrderProductItem's item's name property or OrderServiceItem's item's name property, in an Order, among others. Naturally, in my HQL, I make use of LEFT OUTER JOIN for these like below:

SELECT DISTINCT o 
FROM Order o 
LEFT OUTER JOIN o.store.managers AS m 
LEFT OUTER JOIN o.products AS p 
LEFT OUTER JOIN o.services AS s 
WHERE o.store = :store 
  AND (o.store IS NULL OR m.id = :userId)  
  AND (TRIM(LOWER(o.identifier)) LIKE LOWER(:keywords) OR 
       TRIM(LOWER(o.description)) LIKE LOWER(:keywords) OR 
       TRIM(LOWER(o.customer.name)) LIKE LOWER(:keywords) OR 
       TRIM(LOWER(o.customer.description)) LIKE LOWER(:keywords) OR 
       TRIM(LOWER(o.customer.phone)) LIKE LOWER(:keywords) OR 
       TRIM(LOWER(p.item.name)) LIKE LOWER(:keywords) OR 
       TRIM(LOWER(p.item.product.name)) LIKE LOWER(:keywords) OR 
       TRIM(LOWER(p.item.product.specifications)) LIKE LOWER(:keywords) OR 
       TRIM(LOWER(p.item.product.brand)) LIKE LOWER(:keywords) OR 
       TRIM(LOWER(s.item.name)) LIKE LOWER(:keywords) OR 
       TRIM(LOWER(s.item.specifications)) LIKE LOWER(:keywords)) 
ORDER BY o.orderTime desc

After running this query a few times, I noticed that it only returns Order objects which has both products AND services. In other words, Order objects which has ONLY products and whose item's name property matches the keywords, won't be returned. Or, Order objects which has ONLY services and whose item's name property matches the keywords, won't be returned.

Undoubtedly, I know this must have something to do with the LEFT OUTER JOIN I have for both products and services. But I had thought LEFT OUTER JOIN means it can return rows for which products is NULL or services is NULL too. And given that my WHERE clause involving the keywords is entirely made up of OR sub-clauses, I cannot understand why this suddenly turns into an AND condition for having both products and services be "present" (or non-null) in the Order objects that get returned. I even tried adding p IS NULL and s IS NULL in my OR sub-clauses to no avail.

Can anyone please take a look at my query and see if there's anything I have missed? Thank you!


Solution

  • Have you checked the generated query? I think it happens, because you are accessing p.item and s.item which are loaded with an inner join.