Hi! I'm working on an exhaustive post on composite ids, but it may not be ready for a few days. I don't want to leave you hanging though-here's a bit of an explanation that I've written thus far on <version> in regards to composite ids, I hope it helps:
Note the <version> element. It lets NHibernate know how to tell if an entity is new or not. In the usual scenario where NHibernate manages the ID, NHibernate monitors whether the id value is the original unsaved value and determines whether to Save or Update for you if you call SaveOrUpdate(), a very handy method. If NHibernate is not managing the ID, as is the case in an Assigned ID (think an SSN that you manage) or in composite (where you create the relationships or values yourself that make the id) then it doesn't know how to tell if your id is saved or not-its usual technique doesn't work. So with <version> NHibernate gets a column it has control over, and can safely monitor for an unsaved value. Without this, you'd be unable to use SaveOrUpdate with this element-you'd have to call Save or Update as appropriate-additionally, since ALL cascading functions on collections are essentially NHibernate calling SaveOrUpdate, you're not going to be able to use cascading. Alternatively, if you don't like the <version> column, you could implereally pressingment IInterceptor 's IsTransient() method to get similar functionality. --- A last note: using <key-many-to-one as you've done should generally work, but it's not ideal in a few ways-though these things may not be a problem for you: 1)If you plan to get ShippingDay directly-you won't be able to use the standard ISession.GetById, you'll have to implment your own, through a hand-rolled query, for instance and 2) your composite-id entity will not be able to be cached, so if your app really, really needs this entity to be cached, you could have an issue. The ideal answer is to make a little serializer identifier class and put a class attribute on your <composite-id> tag, but it's a little more involved, and will involve a numer of things to configure beyond what you've already done, unfortunately, and will change your model a bit further. If neither of these issues are important for you you're probably ok as-is, but it's still something to keep in mind, and to address if needed at some point. On Sun, Oct 11, 2009 at 6:56 PM, mathmax <[email protected]>wrote: > > Ok, I've replaced > <key-property name="ShippingMethod" column="shipping_method" /> > with > <key-many-to-one name="ShippingMethod" class="Shipping" > column="shipping_method" /> > and set reverse="true" and cascade="all-delete-orphan" on the bad. > With cascade="all" element cannot be removed. I don't really > understand in which cases we have to use cascade="all" rather than > cascade="all-delete-orphan"... anyway it works like a charm like that. > Could you also tell me why nhibernate needs the <version /> property > when dealing with composite-id ? > > Thank you very much for your help. > > On 11 oct, 23:58, Anne Epstein <[email protected]> wrote: > > Hi again, it's worth noting that if you have the ability to change up > your > > db, you might want to look at using a surrogate key with an idbag, > they're > > more efficient, and have fewer gotchas than composite ids. > > > > I noticed a bit more upon a second look: your bags in Shipping and Days > > don't have inverse="true" .. but you probably want to have that-the > default > > behavior is inverse=false, and that means your shipping_method and day > > columns in your primary key needs to be nullable, as the implication is > that > > DeliveryDay doesn't require shipping_method or days to be meaningful. in > the > > inverse="false" scenario you're running under that's how things are. As > your > > PK is made up of these two things, they are most definitely NOT optional, > > ever. (If either should be optional, take them off your PK) > > inverse=false works like this: NH will insert child object w/ parent id > of > > null, insert parent object, update child object with relationship. in > the > > case of deleting the child from the bag, you're talking the reverse, > which > > in thise case removes part of your primary key before your delete. note > that > > with inverse=true you're going to need to explicitly manage this > > relationship on the DeliveryDay object end (ie your DeliveryDay.Day and > > DeliveryDay.ShippingMethod. you can coordinate both directions by having > an > > add method (this is often on the class with the bag end) like this method > in > > ShippingMethod: > > > > AddDeliveryDay(DeliveryDay d) { > > d.ShippingMethod = this; > > _deliveryDays.Add(d); > > > > } > > > > Hopefully that gets you closer, good luck! > > > > On Sun, Oct 11, 2009 at 4:00 PM, mathmax <[email protected] > >wrote: > > > > > > > > > Thank you for your answer. > > > I've added this property in the class and the mapping file and its > > > corresponding field in the table, but I still get this error when > > > deleting an item: > > > could not delete collection rows: > > > [DeliveryDays.Shipping.Days#france_by_tnt_express][SQL: UPDATE > > > delivery_days SET shipping_method = null WHERE shipping_method = ?p0 > > > AND shipping_method = ?p1 AND Day = ?p2]" > > > Should I use unsaved-value="any" ?Should I use cascade="all" ? > > > I'm saving using session.Flush() (not SaveOrUpdate) because I want to > > > save all my entities. > > > This is very strange because the SQL sent is an UPDATE and not a > > > DELETE... > > > Am I doing something wrong ? > > > > > Best regards, > > > > > mathmax > > > > > On 11 oct, 17:55, Anne Epstein <[email protected]> wrote: > > > > Composite keys are finicky. One key annoyance with them is that > unless > > > you > > > > givroperly.e them a little help, composite-key entities can't > properly > > > use > > > > SaveOrUpdate, and can't be used with cascading properly. > > > > > > Two fixes. you can implement an IInterceptor, or you can add a > <version> > > > > property. I haven't done the first, but I can say from experience > the > > > > second is simple enough. Just a few things you need to do: > > > > > > 1) add a property to your class-just like any of your other > properties in > > > > your class... unless you do choose to use access="field..." in this > case, > > > > let's name your property LastModifiedOn. this new field is for > > > NHibernate, > > > > not for you, so you can look, but don't mess with it yourself. (you > may > > > > want to use access="field...." option so you man map directly to the > > > private > > > > field and you don't have to expose this outside your class-you'll > need to > > > go > > > > to the NH docs and pick your naming strategy)- > > > > > > 2) add property to your mapping: > > > > <version name="LastModifiedOn" type="timestamp" > column="LastModifiedOn" > > > > /> > > > > > > 3) Don't forget to add the new field to the table! Note that if you > add a > > > > version column to an existing table, you're going to have to > prepopulate > > > the > > > > existing rows for your your new column with an initial datetime or > you'll > > > > get errors-any date at all, so long as it's in the past. 1/1/2009 > > > 12:00:00 > > > > is as good a value as any.... > > > > > > On Sun, Oct 11, 2009 at 1:45 AM, mathmax < > [email protected] > > > >wrote: > > > > > > > Hello, > > > > > > > I try to delete an item of a collection inside a transaction. When > I > > > > > flush the session I get this error because the class of the child > > > > > items is mapped with a composite Id. > > > > > > > the mapping file look like this: > > > > > > > <class name="Shipping" table="shipping_methods" lazy="false"> > > > > > <id name="Id" column="id"> > > > > > <generator class="assigned" /> > > > > > </id> > > > > > ... > > > > > <bag name="Days" table="delivery_days" lazy="false" > cascade="all- > > > > > delete-orphan"> > > > > > <key column="shipping_method"/> > > > > > <one-to-many class="DeliveryDay" /> > > > > > </bag> > > > > > > > </class> > > > > > > > <class name="DeliveryDay" table="delivery_days" lazy="false"> > > > > > <!--http://journalofasoftwaredev.wordpress.com/2008/04/19/ > > > > > nhibernate-exception-unexpected-row-count-0-expected-1/--< > > >http://journalofasoftwaredev.wordpress.com/2008/04/19/%0Anhibernate-e.. > .> > > > > > > > <composite-id unsaved-value="any"> > > > > > <key-property name="ShippingMethod" column="shipping_method" > /> > > > > > <key-property name="Day" /> > > > > > </composite-id> > > > > > ... > > > > > </class> > > > > > > > I try with and without unsaved-value="any" on the composite-id tag > > > > > cascade="all-delete-orphan", cascade="all" and no cascade attribute > on > > > > > the bag tag. > > > > > But I always get an error. > > > > > Depending on the cascade and unsaved-value attribute I get > different > > > > > error messages. > > > > > With this mapping file I get this error : {"Unexpected row count: > 0; > > > > > expected: 1"} > > > > > and it seems that the SQL sent to the database is an UPDATE > statement > > > > > instead of a DELETE statement... very strange. > > > > > I also get sometimes this error when I try to add more than on item > to > > > > > the collection : {"a different object with the same identifier > value > > > > > was already associated with the session: DeliveryDays.DeliveryDay, > of > > > > > entity: DeliveryDays.DeliveryDay"} > > > > > > > Does anyone know what I am doing wrong with composite-key ? > > > > > > > regards, > > > > > > > mathmax > > > > > > > --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "nhusers" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/nhusers?hl=en -~----------~----~----~----~------~----~------~--~---
