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.
Related
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..
I have an object hierarchy as follows:
Object
Reports
Report Items
Based on other posts here, I set the Report.ReportItems mapping to Inverse, and CascadeAllDeleteOrphan. This makes it so when I remove a Report from an Object it goes to erase the ReportItems in that report.
The way it does it is very inefficient. It basically does a Delete ... Where ReportItemID = ?.
I've seen suggestions to increase the batch size to prevent this from being done in too many round trips, but that seems like a sloppy fix. Is there a way to make NHibernate generate a query like this instead:
Delete ... Where ReportID = ?
This way it would execute one query that would delete all the ReportItems instead of one statement per ReportItem.
Thanks in advance.
Edit
I have heard from a few people that NHibernate will simply not work this way with standard QueryOver etc.
I decided to start using HQL to fix the problem. I will later use some reflection to make sure there are no "magic strings" being used.
I had the idea to:
Use HQL to mass-delete the Report Items
Tell the Session to Refresh the object so it could detect that the Report Items are gone
Then tell the session to erase the Report, and let it clean up the remaining information
This does not work though. You can see the code below:
Session.CreateQuery("delete ReportItem r where r.Report= :report").SetEntity("report", SelectedReport).ExecuteUpdate()
Session.Refresh(SelectedReport)
Object.Reports.Remove(SelectedReport)
Session.Delete(SelectedReport)
Session.Update(Object)
I have also tried doing a Session.Evict after performing the HQL statements, but NHibernate keeps using the cache to try to delete the ReportItems. Any tips on how to do this?
If your foreign key has delete rule set to cascade then you can tell NH not to generate DELETE statements for child items when the parent is deleted. In this case NH will generate DELETE statement only for parent entity.
<bag name="ReportItems" cascade="all-delete-orphan" inverse="true">
<key column="report_id" on-delete="cascade" />
<one-to-many class="ReportItem" />
</bag>
After a lot of research I have found that this is simply not possible. You can use HQL, raw SQL statements, or rely on database rules to perform the deletes by enforcing a relationship. However, there is no way to tell NHibernate to delete a list of objects based on their owner's ID.
my issue lokks similar to this one: (link)
but i have one-to-many association:
<set name="Fields" cascade="all-delete-orphan" lazy="false" inverse="true">
<key column="[TEMPLATE_ID]"></key>
<one-to-many class="MyNamespace.Field, MyLibrary"/>
</set>
(i also tried to use )
this mapping is for Template object. this one and the Field object has their ID generators set to identity.
so when i call session.Update for the Template object it works fine, well, almost:
if the Field object has an Id number, UPDATE sql request is called, if the Id is 0, the INSERT is performed. But if i delete a Field object from the collection it has no effect for the Database. I found that if i also call session.Delete for this Field object, everything will be ok, but due to client-server architecture i don't know what to delete.
so i decided to delete all the collection elements from the DB and call session.Update with a new collection. and i've got an issue: nhibernate performs the UPDATE operation for the Field objects that has non-zero Id, but they are removed from DB!
maybe i should use some other Id generator or smth..
what is the best way to make nhibernate perform "delete all"/"insert all" routine for the collection?
Is the entity you are updateing already associated with the session? (ie do you load the entity and modify that loaded instance)?
It sound like you are trying to tell nhibernate to update a detached entity, in this case nhiberante cannot know what entities as been added/removed in the collection. In this case you could use Merge:
var mergedEntity = session.Merge(entityPasedFromClient)
The merge operation will fetch the enity from the db compare it with the one that as been sent from the client and merge them, that way the entity that nhiberante fetch from the db (and is associated with the session) is modified and later fetched, the merged entity is returned (this will not be the same instance as the entity you pass the merge operation).
I am not sure I understand the last part of your question:
"so i decided to delete all the collection elements from the DB and call session.Update with a new collection. and i've got an issue: nhibernate performs the UPDATE operation for the Field objects that has non-zero Id, but they are removed from DB!"
Are the field items updated and then removed?
I have two tables - Run and Sample, where Sample.RunId is a foreign key linking samples to their runs.
I would like to have a SampleCount property in Run, which is the number of Sample objects associated with the particular Run.
Is it possible to map such a property in NHibernate mapping of the Run type?
Thanks.
Although the collection solution by Diego Mijelshon is perfectly valid it does mean an extra query after fetching the Run entity. If you haven't mapped the collection and you do not want to or you do not want the additional query, consider a "computed" property as such
in the Run class mapping
<property name="SamplesCount" type="long" formula="(select count(s.Id) from Samples s where s.RunId = Id)" />
and in the class Run just add
long SamplesCount {get; set;}
Note that in the query, for the part "s.RunId = Id" NHibernate will insert the proper alias for the root table. Also, don't forget the parenthesis, it makes it easier for the parser and in some cases is required.
This approach has the benefit of applying a subquery on the select (which may or may not be good depending on your case). The property can also be lazily-loaded (i think a NH 2++ feature) if this property is something you will only rarely need.
While you can do that using a <join>, it's not a good idea.
Instead, declare your Run.Samples collection as lazy="extra", and accessing Run.Samples.Count will do a query for the count instead of loading the entire collection.
I'm writing an application and started to test my domain model entities. If I Create an instance of entity Company like this var company = new Company("my company"); I should get a valid entity, meaning the company should at this moment have an Id correct?
So the problem is that at the moment I have the Id generation made in the DB defined in an hbm file like this:
<id name="ObjectIdentity" column="CompanyId" type="System.Guid" unsaved-value="00000000-0000-0000-0000-000000000000">
<generator class="guid.comb"/>
</id>
This causes a problem when writing unittests as I do not have en entity with an Id as it dosen't touch the db in the test, aka I have an invalid entity.
Now should I assign Id in the application and not let nhibernate be in charge of this or is this the wrong way to do it?
In most cases you should be letting NHibernate do its job which is to handle persistence. This is important since it allows you to easily change things (we went from identity to hilo mid-project).
I would question why you care that a newly created object has an id or not? From a business point of view a persistence ID is not relevant and shouldn't be checked with via unit tests. This as mentioned is the domain of integration tests. You should be careful in how you are using an objects persistence Id throughout the rest of your application. Remember this should NOT be treated as the objects business id/key.
You need to call Session.Save on the entity before a guid is generated. You can call Session.Save to generate the entity without actually saving it to the database. This article does a pretty decent job of explaining it
Neither NHibernate nor the Application should be handling identifiers. Leave it to the database since this is the only concrete store of your data, and it is the only part of your application that knows what IDs have already been assigned and what ones are available.
Make an identity primary key column on your database table:
CREATE TABLE dbo.sample (
id int primary key identity(1,1),
...
...
...)
map your entities like this:
<id name="ID" column="id">
<generator class="identity" />
</id>
The Primary key will be generated automatically by the database when you save a new entity for the first time. IDENTITY(1,1) means "give new rows an Identity start at '1' then each subsequent row gets incremented by 1": so 1,2,3,4,5,6,7
You need to save the entity to get an id if you're using db generated identity,
if you wish to create your own identities - it is fine.
do what suits you, just remember what you have chosen when you test.
I usually decide on each class which is more suitable for me - either DB generated or my own.
Nhibernate should generate Id's for you. Id property of the entity must be protected. The best way is to use hilo