0
votes

I'm struggling to replicate a simple sql left join in a nHibernate query. Other answers on SO have led me to be more confused as to what is the smartest way to tackle left joins in a domain query.

Example:

2 DB Tables:

Customer
CustId  INT PK

Orders
OrderId INT PK
CustId  INT FK
Status  INT

1 SQL Query:

Select c.CustId from Customer c
left join Orders o on o.CustId = c.CustId and o.Status = 2
where o.OrderId is null

This will retrieve a unique list of Customers who don't have an order in status 2, Note, it also includes customers who don't have an order at all. This is a contrived example to simplify this question, but this type of query is very useful and not easy to do any other way.

Imagine nh mappings for "Customer" and "Orders" which simply reflect the example tables above.

Is there a simple way to extract my list of unique, non-status-2 customers in nHibernate, in a query, without resorting to a SQL query or ending up in a select n+1 scenario?

Query preferences:

1 linq-to-nhibernate
2 QueryOver
3 HQL
4 Criteria.

Thanks.

3

3 Answers

2
votes

See http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/queryhql.html#queryhql-joins. It's the Hibernate reference, and not the nHibernate reference, but I'd assume they work the same (BTW, this post seems to confirm it):

You may supply extra join conditions using the HQL with keyword.

from Cat as cat left join cat.kittens as kitten with kitten.bodyWeight > 10.0

So, in your case, it should look like

select c.CustId from Customer c
left join Orders o with o.Status = 2
1
votes

NHibernate 3.0 has an overload method for ICriteria .CreateAlias which takes 4 params, the last param is withClause.

Here is an example:

 DetachedCriteria criteria = DetachedCriteria.For<Models.BO.Customer>("customer")
            .CreateAlias(ReflectionHelper.PropertyName<Models.BO.Customer>(x => ((Models.BO.Interfaces.ICustomerQueryOnly) x).Tasks),
                "activeTasks", JoinType.LeftOuterJoin, Restrictions.IsNotNull("activeTasks.LockedBy")
            )
            .CreateAlias(ReflectionHelper.PropertyName<Models.BO.Customer>(x => ((Models.BO.Interfaces.ICustomerQueryOnly) x).Tasks2),
                "availableTasks", JoinType.LeftOuterJoin,
                availableTasksRestraction
            )
            .Add(Restrictions.Eq("CustomerBase", _customerBase))
            .Add(Restrictions.Eq("IsActive", true));

which endup with something like:

     FROM Customers c
         left join Tasks t on t.customerId = c.Id and (t.DeletedDate is null and 
t.lockedById is null and [etc])
         left join Tasks activetasks [etc]
     where [...]

In this example I need to extract all customers and number of available tasks and number of active task for each customer.

0
votes

If the entities are unrelated and you don't wish to map a relation, you can use a theta join. See here

Maybe something like

Select c from Customer c, Order o 
where o.CustId = c.CustId and o.Status = 2