Nhibernate N+1 query problem - nhibernate

I need help with NHibernate. I'm using 2.1, but have tried this on 3 as well, with same results. Any help most appreciated!
When doing an ICriteria query using NHibernate, it executes both the query, and then for each result in the query, it executes another query to select an associated object, which is already returned in the initial resultset as I am using eager loading. This is of course resulting is dismal performance. Using the mapping file below, the query Nhibernate generates is exactly as expected below :
exec sp_executesql N'SELECT top 20 this_.ContactCode as ContactC1_48_1_, this_.IsActive as IsActive48_1_, contact2_.ContactCode as ContactC1_47_0_, contact2_.ContactFullName as ContactF2_47_0_ FROM Clients this_ left outer join Contacts contact2_ on this_.ContactCode=contact2_.ContactCode WHERE this_.ContactCode like #p0 and this_.IsActive = #p1 ORDER BY this_.ContactCode asc',N'#p0 nvarchar(7),#p1 bit',#p0=N'DAL001%',#p1=1
This query returns a single record, however is followed immediately with the following query, which is retrieving the details for the related contact object, which is already returned in full in the initial query, and of course, when returning many records, N addition queries are executed. This is completely unexpected!
exec sp_executesql N'SELECT contact0_.ContactCode as ContactC1_47_0_, contact0_.ContactFullName as ContactF2_47_0_ FROM Contacts contact0_ WHERE contact0_.ContactCode=#p0',N'#p0 nvarchar(6)',#p0=N'DAL001'
Lazy loading is switched off. The ICriteria code is as follows :
ICriteria clientsFromContactCodeQuery = session.CreateCriteria<Client>()
.Add(Restrictions.Like("ContactCode", id + "%"))
.Add(Restrictions.Eq("IsActive", true))
.AddOrder(Order.Asc("ContactCode"))
.SetMaxResults(maxResultCount);
var clientsFromContactCodeList = clientsFromContactCodeQuery.List();
I have a simple nhibernate mapping file :
<class name="Contact" table="Contacts" lazy="false">
<id name="ContactCode">
<generator class="assigned" />
</id>
<property name="ContactFullName" />
</class>
<class name="Client" table="Clients" lazy="false">
<id name="ContactCode">
<generator class="assigned" />
</id>
<property name="IsActive" />
<one-to-one
name="Contact"
class="Contact"
lazy="false"
fetch="join"
/>
</class>

Turn on lazy loading and then use HQL queries to prefetch required children.
from Client c
left join fetch c.Contact
where
c.ContactCode like :id
and
c.IsActive eq true
I don't know the order by syntax off the top of my head and the HQL may need to be tweaked a bit, but that's the core of the solution.

Try setting your max_fetch_depth property to 2 or 3 if you have not. Not sure if this works with one-to-one relationships though.
<property name="max_fetch_depth">3</property>

Most bizarrely, after trying various changes to the query, from hql to icriteria formats, nothing worked. However, I noticed some data where the N+1 queries were not being generated. It turns out the primary keys in the contacts and clients table has SPACES at the end, which when removed, fixed the problem!
Very strange, but thanks for the help.

Related

NHibernate simple collection of joined-subclass entities

I'm trying to map a set of joined-subclass entities to a parent entity. The SQL being produced by NHibernate seems to be incorrect. Am I missing anything in my mapping? Is there another way I can approach this problem?
<class name="ResultItem" table="result">
<id name="ID">
<generator class="identity" />
</id>
<many-to-one name="Job" column="JobID"/>
<property name="Timestamp"/>
<many-to-one name="User" column="UserID"/>
<joined-subclass name="ResultItemAttachment" table="result_attachment">
<key column="ID"/>
<property name="Comment"/>
</joined-subclass>
</class>
This is the SQL being generated by NHibernate. It seems as if its getting confused between super and sub class members? The only table with JobID is the result table and not result_attachment.
SELECT attachment0_.JobID as JobID1_,
attachment0_.ID as ID1_,
attachment0_.ID as ID26_0_,
attachment0_1_.JobID as JobID26_0_,
attachment0_1_.`Timestamp` as Timestamp26_0_,
attachment0_1_.UserID as UserID26_0_,
attachment0_.`Comment` as Comment33_0_
FROM result_attachment attachment0_
inner join result attachment0_1_ on attachment0_.ID=attachment0_1_.ID
WHERE attachment0_1_.JobID=?;
Thanks in advance
I'm afraid this is currently a bug in NHibernate (in there since 2.1 :|) apperently there is a fix in the current alpha of 3.2
https://nhibernate.jira.com/browse/NH-1747
Possible work around is to manually fetch the bag at runtime. Hardly ideal, other option would be to try and get the alpha version running but running an alpha in a production environment is hardly a great idea.

How to query a many-to-many collection with NHibernate?

I've been trying to interpret the answers to similar questions but haven't been able to make it work.
I have a list of activities, and each activity as a list of participants. Here are the mappings:
<class name="Activity" lazy="false">
<id name="ID">
<generator class="guid" />
</id>
<list name="Participants">
<key column="Activity" />
<index column="Ord" />
<many-to-many column="Contact" class="Model.Contact" />
</list>
<property name="Timestamp" />
</class>
<class name="Contact" lazy="false">
<id name="ID">
<generator class="guid" />
</id>
<property name="Name" />
</class>
I am currently retrieving the activities ten at a time with this criteria:
var crit = ModelSession.Current.CreateCriteria<Activity>()
.AddOrder(Order.Desc("Timestamp"))
.SetFirstResult(start)
.SetMaxResults(count);
Now I need to retrieve only those activities in which a particular person participated, in other words: where Contact.Name like '%some_name%'. In raw SQL I'd probably write something like this:
select * from Activity where ID in (select p.Activity from Participants p,
Contact c where p.Contact=c.ID and c.Name like '%some_name%')
Any idea how to do this, with HQL or ICriteria, in a way that lets me keep the paged results and ordering? Many thanks!
ChssPly76 response above wasn't quite correct — this is a unidirectional relationship, so there is no "activity" property on the contacts to project — but it got me on the right track.
The correct answer is actually much simpler (in its own answer so I can format it properly):
crit.CreateCriteria("Participants")
.Add(Expression.Like("Name", like));
Added to the criteria from the original question, this says "return a page of activities, ordered by timestamp, where a participant has this name".
Taken a step further, I really wanted any activity where the search term appeared in the list of participants OR in the description of the activity. In this case you really do need the subquery.
var subquery = DetachedCriteria.For<Activity>()
.SetProjection(Projections.Property("ID"))
.CreateCriteria("Participants")
.Add(Expression.Like("Name", search_term));
crit.Add(Expression.Or(
Subqueries.PropertyIn("ID", subquery),
Expression.Like("Description", search_term));
You just need to add an appropriate condition to your criteria:
crit.CreateAlias("Participants", "participant")
.Add( Expression.Like("participant.Name", "%some_name%") );
See Criteria associations chapter of NHibernate documentation for more details
Update You can use DetachedCriteria to specify your Participant conditions as a subquery:
DetachedCriteria subquery = DetachedCriteria.For(typeof(Participant))
.SetProjection(Projections.Property("activity"))
.Add( Expression.Like("name", "%some_name%") );
crit.add( Subqueries.GeAll("ID", subquery) ).List();

How to avoid a join using NHibernate 2.1 per table inheritance

I'm doing some per table inheritance and all is working great- but I'm noticing that when I want the base entity (base table data) NHProf is showing a left outter join on the child entity / (related table)
How can I set the default behavior to only query the needed data - for example: When I want a list of parent elements (and only that data) the query only returns me that element.
right now my mapping is similar to the below:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="FormBase, ClassLibrary1" table="tbl_FormBase">
<id name="BaseID" column="ID" type="Int32" unsaved-value="0">
<generator class="native" />
</id>
<property name="ImportDate" column="ImportDate" type="datetime" not-null="false" />
<joined-subclass table="tbl_Form" name="Form, ClassLibrary1">
<key column="ID"/>
<property name="gendate" column="gendate" type="string" not-null="false" />
</joined-subclass>
</class>
</hibernate-mapping>
And the example where I want all the data back vs ONLY the parent entity is shown below:
Dim r As New FormRepository()
Dim forms As List(Of Form) = r.GetFormCollection().ToList()
Dim fbr As New FormBaseRepository()
Dim fb As List(Of FormBase) = fbr.GetFormBaseCollection().ToList()
You can't. It's called "implicit polymorphism" and it's a rather nice (albeit unwanted in your case :-) ) feature provided by Hibernate. When you query a list of base objects, the actual instances returned are of the actual concrete implementations. Hence the left join is needed for Hibernate to find out whether particular entity is a FormBase or a Form.
Update (too big to fit in comment):
The general issue here is that if you were to trick Hibernate into loading only the base entity you may end up with inconsistent session state. Consider the following:
Form instance (that is persisted to both form_base and form tables) was somehow loaded as FormBase.
You've deleted it.
During flush Hibernate (which thinks we're dealing with FormBase and thus is blissfully unaware that there are 2 tables involved) issues a DELETE FROM form statement which throws an exception as FK is violated.
Implicit polymorphism exists to prevent that from happening - Form is always a Form, never a FormBase. You could, of course, use "table-per-hierarchy" mapping where everything is in the same table and thus no joins are needed but you'll end up with (potentially) a lot of NULL columns and - ergo - inability to specify not-null on children's properties.
All that said, if this is REALLY a huge performance issue for you (which it normally shouldn't be - presumably it's an indexed join), you could try using a native query to just return FormBase instances.

fetch=join to tables to one class NHibernate

I have read some post about fetch=join - http://nhforge.org/blogs/nhibernate/archive/2009/04/09/nhibernate-mapping-lt-many-to-one-gt.aspx (ser4ik.livejournal.com/2505.html)
So I have some question, Forexample I have class
<class name="AttributesInf" table="attr_inf">
<id name="Id">
<generator class="identity"/>
</id>
<property name="Name" />
<property name="Desc"/>
</class>
and
<class name="AttributeValue" table="attr_val">
<id name="Id">
<generator class="identity"/>
</id>
<property name="Value" />
<many-to-one name="AttrName" column="attrId"/>
</class>
If I use this mapping without set fetch="join" I get sql:
Select av.Id, av.Value, av.attrId From attr_val av where av.Id=...()
and after then separate sql queries like:
Select * From attr_inf where Id = av.attrId
So my Result Is:
class AttrinuteInf
{
int Id;
string Name;
string Desc;
}
class AttributeValue
{
int Id;
string Value;
AttributeInf AttrName;
}
If I set fetch="join" then I get one query:
Select u.Id, u.UserName, u.BlogId, b.Id, b.BlogName, b.BlogAuthor, b.BlogMsg
from Users u
Left outer join Blogs b
On u.BlogId=b.Id
Where u.Id = ...
So I expect to get one Class:
class AttributeValue
{
int Id;
string Value;
string Name;
string Desc;
}
But I have the same result as if I not set fetch to "join".
Is this all right?
Are there any way to get properties from class maped as <many-to-one> directly?
(not as AttrName.Name, but simply Name )
Explanation:
Part of mapping set above don't show my real problem.
I want to map some entity as IDictionary<string,AttributeValue>.
I map it as
<map name="Attributes" table="attr_val" lazy="true" cascade="all-delete-orphan" inverse="true">
<key column="item_id"></key>
<index column="name"></index> //I don't have that item in class AttributeValue, that is why I try to get it from other table
<one-to-many class="AttributeValue"/>
</map>
This isn't doing what you think it's doing. Using fetch=join is just there to eager load the many side of the relationship. In both cases you end up with the same objects returned. By default NHibernate will lazy load the related entities (which is why you get the second query). By using fetch=join you are asking for the entire object relationship at once but it will still populate the objects the same as without the fetch=join.
It's not the way you describe it. Your entities do not change depending on what you request.
You will obtain a list of instances of your main entity, with the association to the other being fetched. So if, in your code, you access the association, you will find the values.
If you don't fetch it, you wouldn't be able to access those fields, as they would not have been retrieved from the database.
I don't understand your question.
The 'fetch-join' attribute just defines how the sql that NHibernate generates to retrieve instances of your classes, should look like.
It has nothing to do with 'what' will be returned. NHibernate will translate the recordset (that is the result of the query) to correct instances of your classes.
If you just want to retrieve parts of your entity (like the name for instance), then you'll have to write a HQL query, or use the ICriteria API and maybe use projections.

NHibernate fetch="join" mapping attribute does not appear to work

Mapping a dictionary with NH. Declaration is as follows:
<hibernate-mapping ...
<map
name="CostsByRole"
table="JobAccountingSnapshotCosts"
lazy="false"
fetch="join"
access="nosetter.camelcase-underscore">
<key column="SnapshotId" />
<index column="RoleCode" type="String" />
<element column="Amount" type="Decimal" />
</map>
</hibernate-mapping>
I am expecting a single SQL query to be generated but instead I get two: a select for the actual object, followed by a select for the contents of the dictionary.
Any ideas?
HQL queries do not consider the values set for fetch in the mapping. You need to specify them exclusively in each HQL query. Its supposedly by design. The fetch attributes value is used only by Criteria queries and by Load/Get.
Assuming it's not a typo on submission, the problem is likely to be the join="fetch" part in your mapping. It should be fetch="join" and since the default for fetch is "select", that would yield your sequential select problem.