简体   繁体   中英

Criteria API With clause in many-to-many relationship

NHibernate 3 introduced the With clause for the Criteria API. I'm trying to use it to restrict the join on a many-to-many relationship and NHibernate is applying the additional join restriction to the linking table rather than the correct table.

I have a many-to-many relationship between Player and Address and I want to return a Player and its mailing address by restricting Address.IsMailingAddress. Restricting IsMailingAddress in the where clause does not return records that do not have a mailing address (or no address at all), so I need to restrict it in the join. The code below is simplified and I can't use HQL.

The query:

var target = session.CreateCriteria<Player>()
    .SetProjection(Projections.Property("PlayerId"))
    .CreateAlias("Addresses", "ad", JoinType.LeftOuterJoin, Restrictions.Eq("ad.IsMailingAddress", true))
    .Add(Restrictions.Eq("LastName", "Anonymous"))
   .List();

generates the SQL:

SELECT this_.PlayerId as y0_
FROM   dbo.VPlayerExisting this_
       left outer join dbo.LinkPlayAddr addresses3_
         on this_.PlayerId = addresses3_.PlayerId
            and (ad1_.MailingAddressFlag = 1 /* @p0 */)
       left outer join dbo.VAddress ad1_
         on addresses3_.AddressId = ad1_.AddressId
            and (ad1_.MailingAddressFlag = 'Anonymous' /* @p1 */)
WHERE  this_.Name_Last = @p2

MailingAddressFlag (the column that's mapped to IsMailingAddress) is restricted in the many-to-many linking table where it doesn't exist and the LastName restriction is applied in the join to the Address table. The query does get issued but of course the database server raises an exception.

Is this a bug, unsupported or am I doing it wrong?

var target = session.CreateCriteria<Player>()
    .CreateAlias("Addresses", "ad", JoinType.InnerJoin)
    .Add(Restrictions.Eq("LastName", "Anonymous"))
    .Add(Restrictions.Eq("ad.IsMailingAddress",true))
    .List();

Just inner join to mailing address with the restriction on IsMailingAddress?

Should bring back Players (where LastName is "Anonymous") with their MailingAddress where they have an Address and that address is flagged as IsMailingAddress.

The solution is below. There should be just one address with IsMailingAddress set, so the or condition allows the join to succeed if there is one or none using an inner join.

var target = session.CreateCriteria<Player>()
    .SetProjection(Projections.Property("PlayerId"))
    .CreateAlias("Addresses", "ad", JoinType.InnerJoin)
    .Add(RestrictionsOr(Restrictions.Eq("ad.IsMailingAddress", true), Restrictions.IsNull("ad.AddressId")))
    .Add(Restrictions.Eq("LastName", "Anonymous"))
   .List();

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