I would not recommend that you use the NHibernate "bag" mapping option for a many-to-many association, for example:


Never mind the strange domain, it is for a upcoming NHibernate presentation and I was too bored with the normal Order > OrderLines example domains.

The reason why using a bag for many-to-many is not recommended is because of the poor update behavior you get. If you were to load an entity (RebelEncounter in this case) that has a ShipsLost many-to-many association and just add another StarDestroyer to the ShipsLost collection like this:


This would be issued to the database:


As you can see all the existing ships where deleted and then reinserted (along with the single new one you added). You can probably guess that this is far from ideal from a performance stand point. However it is easy to fix. First we have to change the mapping from bag to set:


NHibernate will use the HashedSet type from the Iese.Collection framework as the collection type when using the set mapping, that means that the type for the property can no longer be IList<StarDestroyer> since HashedSet does not implement that interface, however it does implement ICollection<T>. So we can change the code to something like this:


With this change NHibernate will now only insert the newly added entity:



Anonymous said...

Sets also result in a correct grouping in a hierarchical result. For example, I have 2 orders each with 2 order details. A bag (default) will give you 4 objects flattened when selecting both orders due to having 4 order details total. A set will give you 2 objects each with 2 order details grouped appropriately.

Anonymous said...

Have you tried to add inverse="true" to one end of the mapping?

I can't come up with conclusion for what I found from NHiberante reference without nhibernate profiler.
17.5.1. Taxonomy

"Bags are the worst case. Since a bag permits duplicate element values and has no index column, no primary key may be defined. NHibernate has no way of distinguishing between duplicate rows. NHibernate resolves this problem by completely removing (in a single DELETE) and recreating the collection whenever it changes. This might be very inefficient."

17.5.2. Lists, maps, idbags and sets are the most efficient collections to update
"However, in well-designed NHibernate domain models, we usually see that most collections are in fact one-to-many associations with inverse="true". For these associations, the update is handled by the many-to-one end of the association, and so considerations of collection update performance simply do not apply."

Torkel Ödegaard said...

Yes, of course I have tried that.

As the documentation states, this bad update behavior does not occur for one-to-many and inverse=true, it does occur when using bag and many-to-many

Anonymous said...

Thanks for this great tip. It made my day!

Ashraf said...

I had exact same situation. But it answered in the stackoverflow.com. Thanks.
here is the link if you interested.

Anonymous said...