Can I add many-to-many relationships without initializing the collection in NHibernate?

Here is my class:

public class User { public int Id { get; set; } public string Name { get; set; } public ISet<User> Friends { get; set; } } 

Here is my mapping:

 <?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Test" assembly="test"> <class name="User" table="Users"> <id name="Id" column="id"> <generator class="native"/> </id> <property name="Name" column="name"/> <set name="Friends" table="Friends"> <key column="user_id"/> <many-to-many class="User" column="friend_id"/> </set> </class> </hibernate-mapping> 

Here is the problem:

 User user = session.Load<User>(1); User friend = new User(); friend.Name = "new friend"; user.Friends.Add(friend); 

In the last line [user.Friends.Add (friend)], I noticed that she initializes the Friends collection before adding a new friend to it.

My question is: is there anyway to avoid this behavior in NHibernate? Because I just want to have only one INSERT command to execute for performance reasons.

+4
source share
2 answers

From Hibernate.org

Why does Hibernate always initialize a collection when I only want to add or remove an item?

Unfortunately, the collection API defines method return values ​​that can only be calculated by hitting the database. There are three exceptions to this: Hibernate can add to, or inverse = "true" without initializing the collection; the return value should always be true.

If you want to avoid an additional traffic database (i.e., code is in critical condition), reorganize the model for many-to-one use only. It is almost always possible. Then use queries instead of accessing the collection.

Also, reading this NHibernate blog post and Inverse = True | False Attribute will certainly help.

[Edited]

Well, think of many-to-one and many others. Where is the same. That's why they said to reorganize the model. You need to enter another person, say UserFriend or something like that. Now you will do many-to-one for the user from user to user, from friend to user.

Therefore, this will make it β€œmany to many,” as you can see. Hope this makes refactoring understandable. You may not want to do this if you do not have real performance. As Darin already mentioned, in one of the comments: "Do not do preliminary mature optimization." Next, I want to quote this shameful maxim of Donald E. Knuth: "Premature optimization is the root of all evil."

+2
source

The reason NHibernate issues a SELECT statement when trying to access the Friends collection, because it was not initialized when the user object was loaded. You can force the assembly to load either with the following request:

 User user = session .CreateCriteria(typeof(User)) .SetFetchMode("Friends", FetchMode.Eager) .Add(Expression.IdEq(1)) .UniqueResult<User>(); 

or by adding fetch = "join" to your mapping file:

 <?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Test" assembly="test"> <class name="User" table="Users"> <id name="Id" column="id"> <generator class="native"/> </id> <property name="Name" column="name"/> <set name="Friends" table="Friends" fetch="join"> <key column="user_id"/> <many-to-many class="User" column="friend_id"/> </set> </class> </hibernate-mapping> 

It should be noted that there is a difference between UniqueResult and session.Load. If the user is not found in the database, you will get an ObjectNotFoundException with the second approach, while UniqueResult will return null. You can use any approach based on your needs.

0
source

All Articles