Understanding and using Inverse in nhib.pdf

Source: Internet
Author: User

In the project, nhibing performs ORMapping, which makes database operations very simple, but NHibernate has many features that are not easy to understand. For example, the Inverse function is one of them.

When using nhib.pdf for database operations, such as data insertion, cascade is often used. For example, the most common is that an order corresponds to multiple lines, when saving an order, you only need to Save the order object. All detailed lines under the order will be saved in cascade mode. At the object model level, the Order object has an attribute IList <OrderItem> Items, corresponding to the Order details OrderItem. For the OrderItem object, there can be no reference to the Order object. If there is a reference to the Order object, it is a Bidirectional Association of Bidirectional!

For Bidirectional, a problem occurs when saving data to the database. If the data on both sides is inconsistent, that is, mismatch, is it based on the Items in Order or the Order in OrderItem? In the nhib1_cookbook, we say this:

To work around this mismatch, nhib1_ignores one side of the bidirectional relationship. the foreign key in the database is populated based on either the OrderItems reference to the Order or the Orders collection of OrderItems, but not both. we determine which end of the relationship controls the foreign key using the inverse attribute on the collection. by default, the Order controls the foreign key. saving a new Order with one OrderItem will result in the following three SQL statements:

INSERT INTO "Order" (Id) VALUES (@p0) INSERT INTO OrderItem (Id) VALUES (@p0) UPDATE OrderItem SET OrderId = @p0 WHERE Id = @p1 

When we specify inverse = "true", the OrderItem controls the foreign key. This is preferable because it eliminates the extra UPDATE statement, resulting in the following two SQL statements:

INSERT INTO "Order" (Id) VALUES (@p0) INSERT INTO OrderItem (OrderId, Id) VALUES (@p0, @p1)

In general, nhib.pdf uses the attribute of Order as the Valid Association by default. In other words, you only need to add orderitems one by one to the Items set of Order,Final ResultYou do not need to care about what the Order referenced in OrderItem is or is empty. If inverse = "true" is set when Order Item is configured in Mapping, nhib.pdf uses Order reference of OrderItem as association.

The difference in details can be seen in SQL statements. When the default Inverse is false, the OrderId field of the database is set to null when OrderItem is saved, then Update the Order Id to OrderItem.

[Note: this is the final result, not the intermediate result. During the Insert OrderItem operation, its OrderId is the Id of the Order object corresponding to the object. If the Order object is not saved, then, OrderId is null. If it is saved, it is the Id of the Order, and then the OrderId will be updated.]

 

Next, let's take a specific example: department and employee, one-to-multiple relationship. Department D1, D2, employee U1, U2, and D1 Users have U1 and U2, And the U1 object references D1, u2 object references D2.

Department d1=new Department(){Name = "D1"};Department d2=new Department(){Name = "D2"};User u1=new User(){Name = "U1",Department = d1};User u2=new User(){Name = "U2",Department = d2};d1.Users=new List<User>(){u1,u2};

If Inverse is not set by default, if d1 is saved first and d2 is saved, the following SQL statement is generated:

NHibernate: INSERT INTO DEPARTMENT (NAME, DEPARTMENT_ID) VALUES (@p0, @p1);@p0 = 'D1' [Type: String (0)], @p1 = 100000000100000 [Type: Int64 (0)]NHibernate: INSERT INTO USER (NAME, DEPARTMENT_ID, USER_ID) VALUES (@p0, @p1, @p2);@p0 = 'U1' [Type: String (0)], @p1 = 100000000100000 [Type: Int64 (0)], @p2 = 100000000100000 [Type: Int64 (0)]NHibernate: INSERT INTO USER (NAME, DEPARTMENT_ID, USER_ID) VALUES (@p0, @p1, @p2);@p0 = 'U2' [Type: String (0)], @p1 = NULL [Type: Int64 (0)], @p2 = 100000000100001 [Type: Int64 (0)]NHibernate: INSERT INTO DEPARTMENT (NAME, DEPARTMENT_ID) VALUES (@p0, @p1);@p0 = 'D2' [Type: String (0)], @p1 = 100000000100001 [Type: Int64 (0)]NHibernate: UPDATE USER SET NAME = @p0, DEPARTMENT_ID = @p1 WHERE USER_ID = @p2;@p0 = 'U2' [Type: String (0)], @p1 = 100000000100001 [Type: Int64 (0)], @p2 = 100000000100001 [Type: Int64 (0)]NHibernate: UPDATE USER SET DEPARTMENT_ID = @p0 WHERE USER_ID = @p1;@p0 = 100000000100000 [Type: Int64 (0)], @p1 = 100000000100000 [Type: Int64 (0)]NHibernate: UPDATE USER SET DEPARTMENT_ID = @p0 WHERE USER_ID = @p1;@p0 = 100000000100000 [Type: Int64 (0)], @p1 = 100000000100001 [Type: Int64 (0)]

After carefully analyzing these SQL statements, we will find that when insert saves U1, its primary mentid has a value, while when Insert saves U2, its primary mentid is null, this is because D2 has not been saved to the database yet, and there is no Id, so insert Null. Next, save D2. After D2 is saved, there is an Id, so you need to update the worker mentid of U2, make it equal to the D2 Id. The above are the insertion processes. Next, we need to perform the foreign key Update operation to ensure that the foreign key in the database is consistent with the Users set in the Department in the object, so we can Update each User table.

If it is changed to Inverse = True, then save d1 and d2, then the corresponding SQL is:

NHibernate: INSERT INTO DEPARTMENT (NAME, DEPARTMENT_ID) VALUES (@p0, @p1);@p0 = 'D1' [Type: String (0)], @p1 = 100000000100000 [Type: Int64 (0)]NHibernate: INSERT INTO USER (NAME, DEPARTMENT_ID, USER_ID) VALUES (@p0, @p1, @p2);@p0 = 'U1' [Type: String (0)], @p1 = 100000000100000 [Type: Int64 (0)], @p2 = 100000000100000 [Type: Int64 (0)]NHibernate: INSERT INTO USER (NAME, DEPARTMENT_ID, USER_ID) VALUES (@p0, @p1, @p2);@p0 = 'U2' [Type: String (0)], @p1 = NULL [Type: Int64 (0)], @p2 = 100000000100001 [Type: Int64 (0)]NHibernate: INSERT INTO DEPARTMENT (NAME, DEPARTMENT_ID) VALUES (@p0, @p1);@p0 = 'D2' [Type: String (0)], @p1 = 100000000100001 [Type: Int64 (0)]NHibernate: UPDATE USER SET NAME = @p0, DEPARTMENT_ID = @p1 WHERE USER_ID = @p2;@p0 = 'U2' [Type: String (0)], @p1 = 100000000100001 [Type: Int64 (0)], @p2 = 100000000100001 [Type: Int64 (0)]

We can see that the biggest difference is that there is no last two SQL statements for updating Foreign keys. If we adjust the order of storage, save D2 first, and then save D1, then the corresponding SQL statement is:

NHibernate: INSERT INTO DEPARTMENT (NAME, DEPARTMENT_ID) VALUES (@p0, @p1);@p0 = 'D2' [Type: String (0)], @p1 = 100000000100000 [Type: Int64 (0)]NHibernate: INSERT INTO DEPARTMENT (NAME, DEPARTMENT_ID) VALUES (@p0, @p1);@p0 = 'D1' [Type: String (0)], @p1 = 100000000100001 [Type: Int64 (0)]NHibernate: INSERT INTO USER (NAME, DEPARTMENT_ID, USER_ID) VALUES (@p0, @p1, @p2);@p0 = 'U1' [Type: String (0)], @p1 = 100000000100001 [Type: Int64 (0)], @p2 = 100000000100000 [Type: Int64 (0)]NHibernate: INSERT INTO USER (NAME, DEPARTMENT_ID, USER_ID) VALUES (@p0, @p1, @p2);@p0 = 'U2' [Type: String (0)], @p1 = 100000000100000 [Type: Int64 (0)], @p2 = 100000000100001 [Type: Int64 (0)]

Obviously, the first SQL statement performs the update operation on the foreign key, which is less efficient than the second three times. In addition, you must set the OrderId of OrderItem in the database to be null. This is unreasonable for the database model!

Therefore, we recommend that you set Inverse to True during Mapping. In the Code, you also need to set the OrderItem reference to Order.

Inverse is more useful in manytoyun. If the Inverse on both sides is set to False, ManyToMany is set and saved on any side. If both sides are set, it is saved multiple times. For example, if there are employees E1 and E2, And the prizes A1 and A2 are many-to-many relationships, if you want to set E1 employees to receive A1 and A2 awards, you need to set their respective sets:

   1:   Emp e1=new Emp(){Name = "E1"};
   2:   Emp e2 = new Emp() { Name = "E2" };
   3:   Award a1=new Award(){Name = "A1"};
   4:   Award a2 = new Award() { Name = "A2" };
   5:   e1.Awards=new List<Award>(){a1,a2};
   6:   a1.Emps=new List<Emp>(){e1};
   7:   a2.Emps = new List<Emp>() { e1 };

In DomainModel, this setting is correct, but there is a problem with generating SQL:

   1:  NHibernate: INSERT INTO EMP (NAME, EMP_ID) VALUES (@p0, @p1);@p0 = 'E1' [Type: String (0)], @p1 = 1000000001 [Type: Int64 (0)]
   2:  NHibernate: INSERT INTO AWARD (NAME, AWARD_ID) VALUES (@p0, @p1);@p0 = 'A1' [Type: String (0)], @p1 = 1000000001 [Type: Int64 (0)]
   3:  NHibernate: INSERT INTO AWARD (NAME, AWARD_ID) VALUES (@p0, @p1);@p0 = 'A2' [Type: String (0)], @p1 = 1000000002 [Type: Int64 (0)]
   4:  NHibernate: INSERT INTO EMP (NAME, EMP_ID) VALUES (@p0, @p1);@p0 = 'E2' [Type: String (0)], @p1 = 1000000002 [Type: Int64 (0)]
   5:  NHibernate: INSERT INTO AWARD_EMP (EMP_ID, AWARD_ID) VALUES (@p0, @p1);@p0 = 1000000001 [Type: Int64 (0)], @p1 = 1000000001 [Type: Int64 (0)]
   6:  NHibernate: INSERT INTO AWARD_EMP (EMP_ID, AWARD_ID) VALUES (@p0, @p1);@p0 = 1000000001 [Type: Int64 (0)], @p1 = 1000000002 [Type: Int64 (0)]
   7:  NHibernate: INSERT INTO AWARD_EMP (AWARD_ID, EMP_ID) VALUES (@p0, @p1);@p0 = 1000000001 [Type: Int64 (0)], @p1 = 1000000001 [Type: Int64 (0)]
   8:  NHibernate: INSERT INTO AWARD_EMP (AWARD_ID, EMP_ID) VALUES (@p0, @p1);@p0 = 1000000002 [Type: Int64 (0)], @p1 = 1000000001 [Type: Int64 (0)]

It is clear that two records should be inserted into the middle table, but the rows 5-8 have changed to four records inserted. If the Union primary key is set for the intermediate table, an error is returned and insertion fails.

In this case, you can set Inverse = True on the Award side and Inverse = False on the Emp side, indicating that many-to-many relationships are not maintained by the Award side and are only maintained on the Emp side:

public class AwardMapping : IAutoMappingOverride<Award>{    public void Override(AutoMapping<Award> mapping)    {        mapping.HasManyToMany(a => a.Emps).Inverse();    }} public class EmpMapping : IAutoMappingOverride<Emp>{    public void Override(AutoMapping<Emp> mapping)    {        mapping.HasManyToMany(a => a.Awards).Not.Inverse();    }}

After Mapping is set, the correct SQL statement can be generated. Of course, if lines 6 and 7 in C # code are removed, the result is correct, because now the system only recognizes the Awards set in Emp. However, if you delete 5th rows, you cannot retain 6-7 rows.

Summary:

Inverse is used to set the object that Nhibernate depends on when setting the foreign key for Bidirectional Association. The default value is Inverse = False. one-to-many pairs indicate the set that depends on one end, if it is set to True, the reference to one end object is dependent on the multipart object.

For many-to-many pairs, the Inverse at both ends cannot be set to False, which may result in repeated data insertion. One end must be set to False and the other end to True.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.