NHibernate: Criteria expression to retrieve all entities with null count child collection - nhibernate

In nhibernate, I have two classes that are associated with a many-to-one mapping:
<class name="Employee" table="Employee">
..
<bag name="orgUnits">
<key column="id" />
<one-to-many name="OrgUnit" class="OrgUnit">
</bag>
..
</class>
I would like to use a criteria expression to get only Employees where the the collection is null (ie no orgunits) , something like this :
IList employeesWithNoOrgUnit = sess.CreateCriteria(typeof(Employee))
.Add( Expression.IsNull("OrgUnits") )
.List();
This doesn't filter the collection as I expect.

Colleague just found one way that works.
IList employeesWithNoOrgUnit = sess.CreateCriteria(typeof(Employee))
.Add( Restrictions.IsEmpty("OrgUnits") )
.List();

Related

NHibernate many-to-one with lazy false ever executes N queries

I want a list of Purchase searched with some criteria.
Purchase entity contains one Product.
This is the mappings:
<class name="Product" table="Products">
<id name="Id">
<generator class="identity"></generator>
</id>
<property name="Name"></property>
<many-to-one name="Brand" column="BrandId" lazy="false" fetch="join" />
<many-to-one name="Category" column="CategoryId" lazy="false" fetch="join" />
</class>
<class name="Shop" table="Shops">
<id name="Id">
<generator class="identity"></generator>
</id>
<property name="Name"></property>
</class>
<class name="Purchase" table="Purchases" lazy="false">
<id name="Id">
<generator class="identity"></generator>
</id>
<many-to-one name="Product" column="ProductId" lazy="false" fetch="join" />
<many-to-one name="Shop" column="ShopId" lazy="false" fetch="join" />
</class>
And this is the way as I perform the search:
string query = string.Format(#"
select pc from Purchase as pc
inner join pc.Product as p
left outer join p.Brand as b
where
{0} and {1}
and
(
(p.Name like '%{2}%')
or (b.Name like '%{2}%')
)
",
filters.From.HasValue ? "pc.Date >= " + SqlHelper.GetSqlDate(filters.From) : "1=1",
filters.To.HasValue ? "pc.Date <= " + SqlHelper.GetSqlDate(filters.To) : "1=1",
filters.Text);
var list = session.CreateQuery(query)
.List<Purchase>(); //.Future<Purchase>().ToList();
return list;
(I didn't remove other filters on query just to "show" that a simple Get/Query/QueryOver is not possible (it is not readable as a SQL or HQL)
It can be improved... but the problem here is that this code produce 1 query for purchase list and N queries for Product plus M queries for Shop, for example 251 or 770 queries was executed instead of one.
I aspect that just one query was executed, because of lazy="false" on the many-to-one relation.
Why NHibernate make a query on Product (and on Shop) for every Purchase?
Ho can I change the mapping to obtain the execution of one query?
Thanks,
Alessandro
HQL does not respect fetch="join" (in the mapping). I explicit it adding fetch in the join:
select pc from Purchase as pc
inner join **fetch** pc.Product as p
left outer join **fetch** p.Brand as b
... and it works: only one query is executed.

NHibernate inner join gives "Path expected for join"

I have three Tables:
- Person
- User
- PersonSecret
where PersonSecret reference to Person and User:
<class name="PersonSecret" table="PersonSecret" lazy="false" >
<id name="Id" column="Id" type="Guid">
<generator class="assigned"/>
</id>
...
<many-to-one name="Person" class="Person" foreign-key="FK_Person_PersonSecret" lazy="proxy" fetch="select">
<column name="PersonId"/>
</many-to-one>
<many-to-one name="User" class="User" foreign-key="FK_User_PersonSecret" lazy="proxy" fetch="select">
<column name="UserId"/>
</many-to-one>
This is the mapping from User to PersonSecret:
<set name="PersonSecrets" lazy="true" inverse="true" cascade="save-update" >
<key>
<column name="UserId"/>
</key>
<one-to-many class="PersonSecret"/>
And this from Person to PersonSecret:
<set name="PersonSecrets" lazy="true" inverse="true" cascade="save-update" >
<key>
<column name="PersonId"/>
</key>
<one-to-many class="PersonSecret"/>
Now, i try to select all Persons, which has a Entry in PersonSecret for a specific User:
var query = this.Session.CreateQuery(#"from Person a inner join PersonSecret b
where b.UserId = :userId and b.RemindeBirthday = :remind");
This gives me now the ExceptionMessage: "Path expected for join"
Can someone help me, what I am doing wrong? - Thanks.
There are a couple of issues with your HQL query:
When using HQL you need to reference the names of the properties on your model classes instead of the column names they map to. In this case you should reference the PersonSecret.User.Id property in your where clause, instead of the UserId column.
You should also specify the Person.PersonSecrets property in the join clause, otherwise NHibernate won't know which columns to join on.
You should specify your additional filter as a join condition using the with keyword instead of in the where clause.
Here's the correct version:
var query = this.Session.CreateQuery(
#"from Person as a inner join a.PersonSecrets as b with b.User.Id = :userId and b.RemindeBirthday = :remind");
Related resources:
HQL: The Hibernate Query Language - Associations and joins
you have forgotten the "ON" term.
inner join PersonSecret b ON b.PersonId = a.PersonId

Multiple many-to-one to a single table

i have a Product table with two many-to-one references (Title & Description) to a single table named TextRef :
Product:
<many-to-one lazy="false" name="Title" class="TextRef" column="TitleRef" fetch="join" cascade="save-update"/>
<many-to-one lazy="false" name="Description" class="TextRef" column="DescriptionRef" fetch="join" cascade="save-update"/>
every TextRef has a one-to-many to TextRefItem table :
<bag lazy="false" name="Values" table="TextRefItem" cascade="save-update" inverse="true" fetch="join">
<key column="TextId"></key>
<one-to-many class="TextRefItem"/>
</bag>
now i want to load all of TextRefItem(s) for Title & Description in one go but NHibernate only create a join with the first reference (Title)
SELECT this_.ProductId as ProductId7_2_, this_.CategoryId as CategoryId7_2_,
this_.DescriptionRef as Descript3_7_2_,
textref2_.TextId as TextId8_0_, values3_.TextId as TextId4_,
values3_.TextItemId as TextItemId4_, values3_.TextItemId as TextItemId9_1_,values3_.LangId as LangId9_1_,
values3_.Text as Text9_1_, values3_.TextId as TextId9_1_
FROM
Product this_
inner join TextRef textref2_ on this_.DescriptionRef=textref2_.TextId
left outer join TextRefItem values3_ on textref2_.TextId=values3_.TextId
WHERE this_.ProductId = 1
for the other one(Description) it makes a separate select query
how could i tell NHibernate to avoid that ?
I don't know why a separate query is made for the second reference (Description). But it is possible to extend fetching strategies via criterias to load a product with all TextRefs and TextRefItems in one shot:
var criteria = session.CreateCriteria(typeof(Product))
.SetFetchMode("Description.Values", FetchMode.Join);
criteria.SetResultTransformer(new DistinctRootEntityResultTransformer());
criteria.Add(Restrictions.Eq("ProdId", 1));
var list = criteria.List<Product>();

NHibernate Criteria for a non-linked table

We are adding an attribute(tags) to a system. The attribute table is just a relation table without any foreign keys.
Is there a way to add to the criteria to generate a where clause for the attribute table on a parent table.
<class name="Account" table="dbo.Account" lazy="true" >
<id name="Id" column="`AccountId`">
<generator class="int"/>
</id>
<property name="Name" column="`Name`" />
<property name="Address" column="`Address`" />
</class>
<class name="Attribute" table="dbo.Attribute" lazy="true" >
<id name="Id" column="`AttributeId`">
<generator class="int"/>
</id>
<property name="Name" column="`Name`" />
<property name="LinkId" column="`LinkId`" />
<property name="Type" column="`Type`" />
</class>
Example Data
Account
1 - Person - Address
2 - Person - Address
Attribute
1 - Attrib1 - 1 - Account
2 - Attrib2 - 1 - Account
3 - Attrib1 - 2 - Account
4 - Attrib1 - 3 - Event
5 - Attrib1 - 4 - Location
Sample of Existing Code
ICriteria crit = session.CreateCriteria(typeof(Account));
crit.Add(Restrictions.Eq("Name", "Some"););
I would like to add the following to the where clause.
AccountId IN (SELECT LinkId FROM Attribute WHERE Name = 'Attrib1')
Why aren't you using an any type mapping for this? it does exactly this: referencing by a primary key and type name...
There might be some mistakes in the following piece of code, but it looks something like this:
DetachedCriteria subquery = DetachedCriteria.For<Attribute>()
.Add(Projections.Property("LinkId"))
.Add(Restrictions.Eq("Name", "Attrib1"))
.Add(Restrictions.Eq("Type", typeof(Account)));
ICriteria crit = session.CreateCriteria(typeof(Account));
.Add(Restrictions.Eq("Name", "Some"))
.Add(Subqueries.PropertyIn("id", subquery));

Hibernate inner join mapping - string where id

I'd like to map the following sql in NHibernate.
Will I need to make a separate entity object i.e RoomTypeVO mapped to tb_tags to do this?
Any help much appreciated.
SELECT
dbo.tb_rooms.id,
dbo.tb_rooms.name,
dbo.tb_tags.name AS 'roomType'
FROM
dbo.tb_rooms
INNER JOIN dbo.tb_tags ON (dbo.tb_rooms.typeID = dbo.tb_tags.id)
<id name="id" column="id">
<generator class="native" />
</id>
<property name="name" />
If you to a straight sql query you do not have to. If you want to use HQL you will have to work with an entity.
But, you can always do sql queries directly.
If you have a mapped entity then you could probably just do something like this:
FROM RoomType
When you refer to 'FROM', are you thinking of something like this?
<property name="totalPrice"
formula="( SELECT SUM (li.quantity*p.price) FROM LineItem li, Product p
WHERE li.productId = p.productId
AND li.customerId = customerId
AND li.orderNumber = orderNumber )"/>