Nhibernate list mapping with not continuous index column - sql-server-2005

I am developping a ASP.NET with VB using NHIBERNATE to map the tables of a pre-existing database (SQL Server 2005). I have a many-to-many relationship between to entities, that I map like this:
<list name="PropName" table="TableHoldingRelation" lazy="false" >
<key column="idEntity1"></key>
<index column ="orderingColumn" ></index>
<many-to-many class="Entity2" column="idEntity2"></many-to-many>
</list>
The mapping works perfectly and the list(of Entity2) its ordered by the selected column.
The problem is that this column is not continuous, as there might be some values missing (ie: 0,1,3,8). NHibernate is leaving those spaces as null/nothing elements. I would want to have the list "compacted", only containing existing elements, ordered by that column.
Can I achieve this without having to update the database? (updating is not a good solution as it probably will happen in the future that some elements get removed)
Thanks in advance for your help.
EDIT: A bit more info in the problem.
The tables/entities in this case refer to Menus and MenuItems. The application that I am working is is a very complex website, with lots of diferent roles. Each role has his unique menus congfiguration, with their unique items. There are even single users with unique settings. My task is to rewrite the .NET clases and mappings, as they are really messy and other things not relevant for this question. So the database design I am mapping is (for this question, obviously there are other tables):
One table holding menus and their attributes(like wich role do they correspond to)
One table holding menuitems and their attributes (like a link they point to)
One table holding de relation menu-menuitem and a "position"/order column inside that menu.
Just in case more insight on the problem was needed.

As you can read here NHibernate Mapping - <list/> - by Ayende, this behaviour is by design. An extract from comments (close to your question):
...Because in general, having NH doing something like that for you can be
bad. There is a meaning to null values.
But broadly, it is because it is not the responsibility of NHibernate
to do so. If you want something like that, you don't need a list, you
need an ordered set...
With this we can try to change your mapping (see the order-by attribute):
<bag name="PropName" table="TableHoldingRelation"
order-by="orderingColumn"
lazy="false" >
<key column="idEntity1"></key>
<many-to-many class="Entity2" column="idEntity2"></many-to-many>
</bag>
But, this mapping won't allow you to insert into that column orderingColumn.
This documentation 24. Best Practices says:
Don't use exotic association mappings.
Good usecases for a real many-to-many associations are rare. Most of
the time you need additional information stored in the "link table".
In this case, it is much better to use two one-to-many associations to
an intermediate link class. In fact, we think that most associations
are one-to-many and many-to-one, you should be careful when using any
other association style and ask yourself if it is really neccessary.
So, maybe introduce the man-in-the middle pairing object, put the management of the OrderBy property there, and use the sorted list..

Related

How do you map a HasOne relationship with nHibernate Fluent mapping and avoid N+1?

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.

NHibernate prevent cascade delete

Suppose I have a class Foo. I also have a view on Foo called Foo_Foo that lists a many-to-many association between Foos. I mapped this association as a simple immutable set on each Foo, with cascade="none":
<set name="association" table="Foo_Foo" cascade="none" mutable="false">
<key column="ParentFoo" />
<many-to-many class="Foo, MyAssembly" column="BaseFoo" />
</set>
However, when I try to delete a Foo, NHibernate tries and rightly fails to delete the Foo.association.
How can I prevent NHibernate from trying to delete the association to a view?
The collection belongs to Foo. You can't share the collection, so there is no need to keep it in the database. Cascade is used to tell NH if the referenced Foos should be also deleted or not.
Why do you want the Foo_Foo records to keep in the database? If this should be a bidirectional many-to-many self reference, it doesn't work like this.
Edit, after understanding the question.
Cascade doesn't work in your case, because it affects only the referenced Foos.
To avoid inserts / updates and deletes of the collection table, you may try one of the following:
First obvious attempt is mutable="false", which you already tried. I don't really understand why it isn't working. You may ask in the Nhibernate user group.
Less obvious, but promising is inverse="true". Inverse tells NH that the collection is mapped somewhere else and doesn't need to be stored from here. So it just omits inserts, but I don't know about deletes.
If this doesn't work, you need to explore more complex solutions. You could map it as a one-to-many of an intermediate entity which references the Foos. The intermediate entity is a mapping to the view. It is immutable (which still may lead to delete statements). In this case, cascade="false" will work (because it is the referenced entity). It will also work configure insert, update and delete sql statements (which are empty), but this is most probably not even necessary.

NHibernate - Get Categories, Count and Top 5 Items in Each Category

In my application I have many articles, each of which belongs to one or more categories. On my homepage I would like to list all categories with the total number of articles it contains underneath which I would like to show the top X article descriptions (based on when the article was posted). It would looks something like the following
Home Decorating (105)
- How to fix Windows
- How to fix Curtains
- How to fix Doors
- How to fix Counter tops
- How to fix sofas
My mappings are pretty simple and can be traversed either from the article or the category - both of which have nhibernate mapping files.
I can do this pretty easily from a stored proc, but for learning purposes would like to accomplish this via NHibernate and was wondering if there is an efficient way to do this or if i'm always going to end up with multiple queries.
Does anyone know how this could be accomplished via NHibernate?
Update
Here are the mappings:
<class name="MyProj.News.Category, MyProj.News">
<id name="Id" column="ID"/>
<bag name="Articles" table="Category_Article" lazy="true">
<key column="CATEGORY_ID"/>
<many-to-many class="MyProj.News.Article, MyProj.News" column="Article_ID"/>
</bag>
</class>
<class name="MyProj.News.Article, MyProj.News">
<id name="id" column="ID"/>
<!-- inverse end -->
<bag name="categories" table="Category_Article" inverse="true" lazy="true">
<key column="ARTICLE_ID"/>
<many-to-many class="MyProj.News.Category, MyProj.News" column="CATEGORY_ID"/>
</bag>
</class>
Yes, it's possible, but with some SQL magic. I'll not give a complete solution here, but hopefully you'll figure this out.
For count, it's best to add new Count property to your Category entity and map it using formula with subquery "select count(*) from Items ...".
For top 5 items, you'll either need to modify your Category.Items collection mapping or create another one, i.e. Category.TopItems. Then add where mapping to filter your result to top 5 rows (for SQL Server you can use something like "row_number() over(order by DateAdded) < 5". There are equivalents for other databases for sure, too.
Perhaps you could consider an alternate approach; rather than trying to create your entire resultset in one query - how about sending multiple queries in a single round-trip?
NHibernate has support for this in the form of multi-queries, multi-criteria, and futures. You could then assemble the results of these smaller queries into a form that makes sense for display.
I should note that hql has recently had support for skip and take constructs added, which you could likely use to construct your top(x) in each category query.
Even with straight SQL, it's not easily done in an efficient manner. To solve this problem in the proper manner, you should be caching the count of articles on the category entity. Any time you can eliminate unnecessary queries to the database, you should.
Doing this is simple enough. When a new article is added, or one is removed, trigger an update for the total count on the category.
This solution is all around the best approach because now the total count of articles exists on the category entity. You can then simply make one call to get the list of top n articles for the category.

Nhibernate many-to-one problem

I've got a problem with mapping many-to-one in the following code:
...
<property name ="CustomerID"/>
<!-- Many-to-one mapping: Customer -->
<many-to-one name="Customer"
class="Customer"
column="CustomerID"
insert="false" update="false"/>
<property name="Date" />
...
You may notice that i have mapped two CustomerID to Customer table. The reason i do this because when i want to create an Order, i just only assign value to CustomerID and other require fields then do save. many-to-one mapping, I just want to get detail of each customerID.
But, the problem is that: after i update customerID of an Order and Executre SaveOrUpdate with Session.Flush() also (I'm using HibernateTemplate), i still got the old figure when accessing to Order.Customer.
i.e:
Order = getOderByID(1);
Order.CustomerID=3 // Suppose value of CustomerId is 1. Now I changed to 3
SaveOrUpdate(Order);
Print(Order.Customer.CustomerID)// it returns 1 which is wrong. It should be 3
Pls help...
Thanks,
I would suggest you to look at this problem from an NHibernate point of view. And not from a relational database view. Let me start with what i feel you should be doing.
var customer = session.Load<Customer>(3);
var order = session.Load<Order>(1);
order.Customer = customer;
//assuming this is a one directional mapping. otherwise you might
//have to do some more steps to disassociate the order from the old
//customers collection and add it to the new customers collection
session.SaveOrUpdate(order);
Now, order.Customer.CustomerID will return 3.
As Serkan suggested, its better and more feasible to work with objects instead of primary keys.
Also, there really shouldnt be any performance impact here. Nhibernate is able to proxy a lot of the associations as long as the classes have virtual public methods. Because of this, as long as you only query for the Id of the customer, it will not generate a separate sql query. The Id is already there with the proxy object.
With regards to the original question, I have a hunch. NHibernate dynamically generates the sql query for the update and the inserts. This case here is of an update. You have explicitly set the CustomerID property to 3. But the Customer property of the order object still points to the customer object with Id 1. So, when NHibernate generates the sql query, it trys to set the value first to 1, as you asked it to. Then it also sees that the Customer is still pointing to the old object, so reset the CustomerId property to 1. I think NHibernate is getting confused with the dual mappings.
There are two things that you can do. First enable the "show_sql" property in the NHibernate configuration.
<nhibernate>
...
<add key="hibernate.show_sql" value="true" />
</nhibernate>
Check what is the sql being generated when you save the order. That will explain things better.
Second, after saving the order, do session.Refresh(order); You can read about the Refresh() method here. Its towards the end of the section 9.2. It will reload the order object with fresh values from the database. Calling order.CustomerID should show what value you have stored in the database.
I think you'd feel much happier in the long run if you try to forget about Id's of entities once you are done with the OR mapping. You are in a different level which you should think in objects only. If I were you I would remove CustomerId property all together.
If you have performance issues try to solve them in NHibernate way, caching etc.
Two things to try
Flushing the session via Session.Dispose if it's NH 2.x otherwise use Flush()
Make your IDs client assigned.
NHibernate will create the IDs for you and try to manage them unless you specifically tell it not to.

CreateQuery and CreateCriteria generating different SQL queries

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.