I'm doing some tests using NHibernate and I currently have two mapped entities, Orders and Products. As always an Order has a collection of Products, and I'm mapping this as:
<bag name="Products" cascade="all" lazy="true">
<key column ="Order_ID" />
<many-to-many class="Product" column="Product_ID" />
</bag>
And on the C# side:
public virtual IList<Product> Products
{
get { return _Products; }
set { _Products = value; }
}
private IList<Product> _Products = new List<Product>();
In the case I have one persisted Order (John's order) in the database with some items in its Products collection (an apple, an orange and a coconut) and I try to add a new item to the collection (a banana) I can see from the SQL output that NHibernate is doing something like this:
INSERT INTO Products_Table (Product_Name, Product_ID) VALUES ('Banana', @Banana_ID)
DELETE FROM Products WHERE Order_ID = @Johns_Order_ID
INSERT INTO Products (Order_ID, Product_ID) VALUES (@Johns_Order_ID, @Apple_ID)
INSERT INTO Products (Order_ID, Product_ID) VALUES (@Johns_Order_ID, @Orange_ID)
INSERT INTO Products (Order_ID, Product_ID) VALUES (@Johns_Order_ID, @Coconut_ID)
INSERT INTO Products (Order_ID, Product_ID) VALUES (@Johns_Order_ID, @Banana_ID)
Instead of simply doing something like this:
INSERT INTO Products_Table (Product_Name, Product_ID) VALUES ('Banana', @Banana_ID)
INSERT INTO Products (Order_ID, Product_ID) VALUES (@Johns_Order_ID, @Banana_ID)
So why does NHibernate delete all associations from the Products table to recreate them again when I'm only trying to insert a new product in it? How to avoid it?
Thanks in advance!
PS: I tried to use the inverse mapping attribute as suggested in this SO Question but even though the new product gets saved its association with an order doesn't.
EDIT:
Here is the code I'm using to modify the products list:
Order order = session.Load<Order>(orderId);
order.Products.Add(new Product() { Name = "Banana" });
using (ITransaction transaction = session.BeginTransaction())
{
session.Update(order);
transaction.Commit();
}
Just use NHibernate's collections the same way you use ordinary .NET collections, but make sure you understand the semantics of bidirectional associations (discussed later) before using them. Collection instances are distinguished in the database by a foreign key to the owning entity.
Note: large NHibernate bags mapped with inverse="false" are inefficient and should be avoided. NHibernate can't create, delete or update rows individually, because there is no key that may be used to identify an individual row. 6.2.1. Collection foreign keys
Note: large NHibernate bags mapped with inverse="false" are inefficient and should be avoided. NHibernate can't create, delete or update rows individually, because there is no key that may be used to identify an individual row.
The NHibernate mapping element used for mapping a collection depends upon the type of interface. By example, a <set> element is used for mapping properties of type ISet . Apart from <set>, there is also <list> , <map> , <bag> , <array> and <primitive-array> mapping elements.
There's already a similar question on SO: NHibernate Many-To-Many Is Deleting All Associations Before Inserting
From NHibernate's documentation:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With