I'm trying to get a many to many relationship to work using Fluent Nhibernate.
I have a Product and a RelatedProduct Table
Product {Id, Name...}
ProductRelated{Id, ProductId, RelatedProductId, relOrder}
and a Product class
The mapping looks like
HasManyToMany(x => x.RelatedProducts)
.Table("ProductRelated")
.ReadOnly()
.ChildOrderBy("relOrder asc")
.ParentKeyColumn("ProductId")
.ChildKeyColumn("RelatedProductId");
When a query is done for Product and the RelatedProducts are lazy loaded I can see that the sorting is applied correctly using the relOrder on the join table.
Session.Query<Product>()
.FetchMany(p => p.Categories)
.FetchMany(p => p.Departments)
Once I add in eager loading of the related products NHibernate tries to sort by a relOrder column on the product itself instead of on the join table.
Session.Query<Product>()
.FetchMany(p => p.Categories)
.FetchMany(p => p.Departments)
.FetchMany(p => p.RelatedProducts)
Any ideas of whats going on here?
Well to answer your question what's going on here?, I would say, you are using: "not-together fitting features" of NHibernate.
A snippet from documentation 6.6. Sorted Collections:
Setting the order-by attribute tells NHibernate ...
Note: that lookup operations on these collections are very slow if they contain more than a few elements.
Note: that the value of the order-by attribute is an SQL ordering, not a HQL ordering!
So, this could be applied only for "standard" lazy loading, becuase this kind of a feature is applied only on a DB side. It is not managing order in the memory.
And the eager fetching, as the counter-part, is a different way how to generate and issue the SQL Statement to DB.
So, eager and order-by will never work together.
*
My NOTE: I simply have to append this. I can't help myself
I.
The Eager fetching is the feature which should be avoided (I never use it, but it's me). There is a better solution and it is setting the BatchSize(), which will reduce the 1+N into 1+(a few) and will keep all the (lazy) featrues, including order-by. Check these if interested:
NHibernate QueryOver with Fetch resulting multiple sql queries and db hits
Is this the right way to eager load child collections in NHibernate
https://stackoverflow.com/questions/18419988/
BatchSize() is supported for HasManyToMany as well: ToManyBase:
/// <summary>Specify the select batch size </summary>
/// <param name="size">Batch size</param>
public T BatchSize(int size) { ...
II.
The many-to-many mapping, while fancy at first look, is not the way I'd suggest. Try to rethink your model and introduce the first-level-citizen: PairingEntity - for the pairing object. It will then use many-to-one and one-to-many mapping which could give us more... e.g. improved querying like Subqueries... try to check these:
How to create NHibernate HasManyToMany relation
many-to-many with extra columns nhibernate
Nhibernate: How to represent Many-To-Many relationships with One-to-Many relationships?
Related
I'm relatively new to using NHibernate and I'm running into a shortcoming I can't seem to work myself around. I have an object tree that I wish to retrieve from the database in a single roundtrip but end up with a cartesian product.
The objects I'm trying to retrieve are called 'AccountGroup', 'Concern', 'Advertiser' and 'Product' and I only wish to get those objects where the active user has permissions for.
My initial query looked like this:
using (var session = OpenSession())
{
return session.Query<AccountGroupEntity>()
.FetchMany(a => a.Planners)
.Where(a => a.Planners.Any(p => p.Id == userId))
.FetchMany(a => a.Concerns)
.ThenFetchMany(c => c.Advertisers)
.ThenFetch(a => a.Products)
.ToList();
}
This won't work as it will return a cartesian product and the resulting entities will contain many duplicates.
However, I have NO idea how to fix this. I've seen the ToFuture() method that will allow me to execute more than one query in the same roundtrip, but I have no clue how to configure my ToFuture() query in such a way that it populates all the child collections properly.
Could anyone shine some light on how I can use ToFuture to fetch the entire tree in a single query without duplicates?
I do have an answer to this topic, solution which I do use. But it at the end means "do not use Fetch" - do it differently. So, please, take it at least as a suggestion.
Check this Q & A:
How to Eager Load Associations without duplication in NHibernate?
Small cite:
Fetching Collections is a difficult operation. It has many side effects (as you realized, when there are fetched more collections). But even with fetching one collection, we are loading many duplicated rows.
Other words, Fetching is a fragil feature, and should be used wisely in very few scenarios, I'd say. So what to use? How to solve that?
Profit from a built in NHibernate feature:
19.1.5. Using batch fetching
NHibernate can make efficient use of batch fetching, that is, NHibernate can load several uninitialized proxies if one proxy is accessed (or collections. Batch fetching is an optimization of the lazy select fetching strategy. There are two ways you can tune batch fetching: on the class and the collection level.
Batch fetching for classes/entities is easier to understand. Imagine you have the following situation at runtime: You have 25 Cat instances loaded in an ISession, each Cat has a reference to its Owner, a Person. The Person class is mapped with a proxy, lazy="true". If you now iterate through all cats and call cat.Owner on each, NHibernate will by default execute 25 SELECT statements, to retrieve the proxied owners. You can tune this behavior by specifying a batch-size in the mapping of Person:
<class name="Person" batch-size="10">...</class>
NHibernate will now execute only three queries, the pattern is 10, 10, 5.
You may also enable batch fetching of collections. For example, if each Person has a lazy collection of Cats, and 10 persons are currently loaded in the ISesssion, iterating through all persons will generate 10 SELECTs, one for every call to person.Cats. If you enable batch fetching for the Cats collection in the mapping of Person, NHibernate can pre-fetch collections:
<class name="Person">
<set name="Cats" batch-size="3">
...
</set>
My experience, this approach is pricless. The setting working for us is batch-size="25".
If you ask for any kind of Entity (via session.Get() or .QueryOver()...) - until session is open, the first time we touch related reference or collection - it is loaded in few batches... No 1 + N SELECT Issue...
Summary: Mark all your classes, and all collection with batch-size="x" (x could be 25). That will support clean queries over root Entities - until session is open, all related stuff is loaded in few SELECTS. The x could be adjusted, for some could be much more higher...
I'm a beginner in NHibernate. I have to write a complex query on say an "Employee" to populate all the associations for Employee based on the where clause. What I'm looking for is similar to this - when you do a Employee.FindById(10) should fill up OwnedDepartment, SubscribedGroups etc.
The Employee model I need to populate is really heavy (many associations with other objects)
but I need to populate only few associations. How do I achieve it using a query over? or any other approaches?
Updated
I was reading about eager loading just now, has it something to do with the loading ? In my map I have not mentioned any loading techniques, so by default all of my employee's child element are getting loaded already. There is a bunch of queries getting triggered underneath.
All the associations are lazy loaded by default. That means that the load is triggered when you access it - that's why so many queries are issued. If you want to eagerly load the data (which means either joining the tables or - rarely - doing additional select queries at once), you have to specify it in your mapping or query, depending how you fetch your data. The concept is generally called "eager fetching".
If you want to get a single Employee by ID, the standard way to do it is using session.Get<Employee>(10) - but that approach means that eager loads need to be specified in the mapping. For mapping by code it will be c.Lazy(CollectionLazy.NoLazy); for collections or c.Lazy(LazyRelation.NoProxy) for many-to-one - see here or here for details.
I prefer specifying that kind of things in the query - just where it is used, not globally for the whole entity, regardless who is fetching and what for. In LINQ provider you have FetchMany(x => x.SubscribedGroups) for collections and Fetch(x => x.OwnedDepartment) for many-to-one relations. You can find similiar options in QueryOver, if that's your choice.
I have 2 tables ATable and AATable where both have a shared Primary Key - ATable.aKey and AATable.aKey to represent a one-to-one relationship. For my Fluent mapping I have a HasOne Relationship defined within my Fluent ATableMapping, all of which works fine. However I have noticed that querying for ATable generates a 2nd query (N+1) for the child Table AATable. My understanding is that Hasone eager loads by default, and I had assumed this would be part of the query for ATable, but I may well have this wrong?
I have researched various solutions including using .Not.LazyLoad().Fetch.Join(), PropertyRef, ForeignKey but I cannot seem to resolve the n+1 so that either it is Eager loaded with 1 query, or Lazy loaded and I can fetch the child with my queries.
Has anyone had any issues with this or have an example they know to work with no n+1? Grateful for any advice.
You have two options:
Not.LazyLoad() which disables possibility to provide lazy loaded related entity and it would enforce NHB to provide corresponding subselect within original query
Use component mapping so both entities point to the same table. This is better approach as once you decided to fetch both entities together, generated queries hit only one table - not two like within first option. This is definitely better for performance.
i am trying to turn off lazyloading for one-to-many mapping in NHibernate. I have the follow mapping in my entity mapping class. An entity has many addresses, and what I was looking for is one query that basically join the base table to the Addresses table and return me all the result in one request. Instead I see a series of sql query submitted to database for each record in the base table.
HasMany(m => m.Addresses).Not.LazyLoad().Fetch.Join();
i need a way to turn off lazyloading completely.
I would strongly suggest to read this blog post by Ayende: NHibernate is lazy, just live with it.
Using ORM and trying to avoid laziness... won't work. In case of addresses you will lose paging for example.
(while fetching them via join, what happens? If there will be entity with 10
addresses, and you will ask for first 10 records... you will get just
one. And it could be worse if you will ask for 11...)
But what you can use, is the power of NHibernate: 19.1.5. Using batch fetching
HasMany(m => m.Address)
...
.Fetch.Select()
.BatchSize(25)
Now, if you will need 25 records, there will be 2 SELECTs. First for entity, second for all the related Addresses. That's improvement, while all the advantages of ORM remain.
I think that would be anough:
HasMany(m => m.Addresses).Not.LazyLoad();
To get the data with select you should explicitly use "Fetch":
session.QueryOver<Item>()
.Fetch(item => item.Addresses).Eager
.Take(1000)
.TransformUsing(Transformers.DistinctRootEntity)
.List();
I assume you load the base entity with a HQL, Linq or plain SQL query. Those queries ignore the "join" fetch settings in the mapping. You have to either explicitly fetch the Adresses in the query or use Get/Criteria/QueryOver.
Reference documentation: http://nhibernate.info/doc/nh/en/index.html#performance-fetching-custom
Client has a Report, Configuration etc properties. A client can have only one of each. Also, each report can belong to only one Client. This is in a way a one-to-one relationship. Therefore my Report table has a foreignkey column clientID. Same for the Configuration and other tables.
Now as per the definition of one-to-one that i read on the nhibernate site, it means that both the primary keys of Report and Client should be the same. Lets just assume that I cannot implement it that way. Hence to simulate the structure that I have in the database, I have the following mappings:
ReportMap
References(x => x.Client, "clientID").Unique().Not.Nullable();
ClientMap
HasOne(x => x.Report).PropertyRef(x => x.Client).LazyLoad().Cascade.SaveUpdate();
Now the problem I am facing is that when I query for a Client, NHibernate is also generating queries to get the Report, Configuration etc... Also, depending on whether I use Criteria or HQL, the generated queries vary wildly.
var client = session.CreateQuery("from Client as c where c.Id = :clientId")
.SetParameter("clientId", 1L)
.UniqueResult<Client>();
generates one query for the client followed by one query for each property that I mapped as HasOne. ie 2 more queries. One for Report and one for Configuration. HQL generates a total of 3 queries.
However, if I use the Load method or Criteria, it generates one query which joins all the concerned tables.
Despite mapping these collections to be lazy loaded, why is NHibernate fetching them? I really only want information from the Client table.
Whats the logical explanation to this?
From the Nhibernate documentaion on fetching strategies, i understand that single associations are lazy proxy fetched. and that the default fetch strategy of select is executed only when the association is accessed. In my case i am not accessing the association. I am simply reading properties that belong to the Client.
All this is so confusing...
Edit1: I have mapped my one to one relation as mentioned in the nhibernate documentation.
http://nhibernate.info/doc/nh/en/index.html#mapping-declaration-onetoone
There are two varieties of one-to-one
association:
* primary key associations
* unique foreign key associations
Alternatively, a foreign key with a unique constraint, from Employee to Person, may be > expressed as:
<many-to-one name="Person" class="Person" column="PERSON_ID" unique="true"/>
And this association may be made bidirectional by adding the following to the Person >mapping:
<one-to-one name="Employee" class="Employee" property-ref="Person"/>
So technically as i understand, i am not doing anything wrong. This scenario is supposed to be supported by nhibernate.
I'm not sure what the problem is with your query, but I suggest you change your mapping. You have is a one-to-many relationship between Client-Report and a business rule that a client can have only one report. You should map it as such. The reports collection can be mapped as a private member and you can expose a Report property on Client to enforce the business rule. I expect that mapping it this way will resolve your query problem.
You could also map it in the other direction witht Client on the many side if that makes more sense.