Minimal and Correct One-to-Many Mapping with NHibernate

I am new to NHibernate and C #, so please be careful!

I have the following two NHibernate objects:

Employee { private long _id; private String _name; private String _empNumber; private IList<Address> _addresses; //Properties... } 

and

 Address { private long _id; private String _addrLine1; private String _addrLine2; private String _city; private String _country; private String _postalCode; //Properties } 

and they have a one-to-many relationship from Employee to Address (each employee can have multiple addresses in their record). Convenient ignoring the fact that more than one employee can live at the same address.

I understand this in terms of objects in memory (NHibernate faces). What I'm struggling with are mapping files (and I take a simple example here). This is what I came up with so far:

 // Intentionally left out XML and <hibernate-mapping> // Mappings for class 'Employee'. --> <class name="Employee" table="Employees"> <id name="ID"> <generator class="native"> </id> <property name="Name" /> <property name="EmpNumber" /> <bag name="Addresses"> <key column="AddressId" /> <one-to-many class="Address" /> </bag> </class> 

and

 // Intentionally left out XML and <hibernate-mapping> . // Mappings for class 'Address' <class name="Address" table="Addresses"> <id name="ID"> <generator class="native"> </id> // Intentionally left out name="Employee" // as I don't have corresponding field in Address entity. <many-to-one class="Employee" column="EmployeeID" cascade="all" /> <property name="AddrLine1" /> <property name="AddrLine2" /> <property name="City" /> <property name="Country" /> <property name="PostalCode" /> </class> 
  • Is it correct?
  • If not, it seems like I'm missing here, this is a field in the Address object, which is a reference to the corresponding Employee object. But if therefore I cannot understand why this is required: I do not need an Address from Employee , just the opposite ...
+6
source share
2 answers

A few tips summarizing the most appropriate standards that I found out when working with NHibernate.

1) If there is a bidirectional link in persitence (DB column), express it in C# code bidirectional .

Other words, if the child has a link to the parent, the parent must have a link to the child.

 public class Employee { ... public virtual IList<Address> { get; set; } } public class Address { ... public virtual Employee Employee { get; set; } } 

This is the business domain as it is. Address belongs to Employee and Employee belongs to address.

If for some reason we really want to limit this, we should probably use the protected modifier, but save the link in C#

2) Use inverse="true" . This can only be used if we have matched both sides (as indicated above) and lead to more “expected and optimized” INSERT and UPDATE sweeps

More details here:

inverse = "true" example and explanation from mkyong

3) Use batch sampling mapping almost always. This will avoid problems with 1 + N during queries. More details:

some details about batch sampling

4) In case one object (in our case Employee) is equal to root (the other without much sense without it) - use cascading. More details:

nhibernate - create a child by updating the parent or create explicitly?

Rules 2,3,4 in display fragments:

 <class name="Employee" ... batch-size="25"> ... <bag name="Addresses" lazy="true" inverse="true" batch-size="25" cascade="all-delete-orphan" > // wrong! This columns is the same as for many-to-one //<key column="AddressId" /> // it is the one column expressing the relation <key column="EmployeeId" /> <one-to-many class="Address" /> </bag> <class name="Address" ... batch-size="25"> ... <many-to-one not-null="true" name="Employee" column="EmployeeID" /> 

3) if we use inverse="true , remember to assign both sides of the relationship (mostly critical when creating)

The reason is this:

we instruct NHibernate - the other side ( Address ) is responsible for the continuing relationship. But for this, it is enough that Address needs a link to Employee - to save its identifier in its column in the address table.

So this should be the standard code to create a new address

 Employee employee = ... // load or create new Address address = new Address { ... Employee = employee, // this is important }; Employee.Addresses.Add(address); session.SaveOrUpdate(employee); // cascade will trigger the rest 

We can also introduce some method like AddAddress() that will hide this complexity, but setting both sides is a good prefix.

+6
source

You must add the all-delete-orphan cascade with respect to one-to-many , if you delete Employee , the address will also be deleted.

If you don't need the Employee link, create an inverse=false relation like this: here

+1
source

Source: https://habr.com/ru/post/986357/


All Articles