tags:

views:

212

answers:

3

Hi, 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,

A: 

Two things to try

  1. Flushing the session via Session.Dispose if it's NH 2.x otherwise use Flush()
  2. Make your IDs client assigned.

NHibernate will create the IDs for you and try to manage them unless you specifically tell it not to.

Chris S
That's very quick, but actually i do call Flush() even before and after it still not update. Thank you anyway. Actually, that is the first time for me tring Nhibernate, but it seems to be strange just small amount of codes.
+1  A: 

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.

Serkan
+3  A: 

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.

Amith George
+1 yes I think session.Load<>() is definitly what the OP needs.
dotjoe
Thank you much. That is a clear explaination.