Coding One to One Entity Associations
PART 2
PART 3
PREREQUISITES & PROJECT SETUP
- NHibernate - Download From Website.
- MUST READ the following article posted in the official NHibernate site where NHibernate can be downloaded itself:
http://nhforge.org/wikis/howtonh/your-first-nhibernate-based-application.aspx
The article mentioned above is THE BEST INTRODUCTION TO SETTING UP NHibernate in Visual Studio and to jumpstart to NHibernate. TO READ MY ARTICLE SERIES, IT IS AN ABSOLUTE PREREQUISITE THAT YOU FOLLOW THE LINK ABOVE AND SET UP THE PROJECT IN VISUAL STUDIO ACCORDING TO GUIDELINES GIVEN IN IT FOR NHIBERNATE USE. The change in the Visual Studio setup between the article link above and sample project available for download in Article 7 is that here we use a SQL Server database instead of a SQL Server Compact Edition database and choose the "SERVICE BASED DATABASE" option when adding the database.
MICROSOFT VISUAL STUDIO 2012 PROFESSIONAL TRIAL VERSION HAS BEEN USED FOR ALL SAMPLES HERE.
SAMPLES
The code samples provided in these articles are useful hints on NHibernate usage. They are not full software editions that will involve handling errors, exceptions and encompass functionalities or domain object fields that mask the NHibernate discussions undertaken as primary task here. They highlight and show the NHibernate part of the code. So most classes and objects will have minimum fields required to highlight NHibernate features only.
Article 7 contains a downloadable console based project of the ECommerce application scenario (simple pocos, just to give the idea of association in an easy to understand way). Examples of all articles are based in this ECommerce scenario. In articles 1 - 5, we will work towards evolving the various object associations for the full sample of Article 7. Since it's console based, it will have only pocos. In Articles 1-5, I have embedded the NHibernate and C# code for establishing the associations from the sample project of Article 7 in the article itself with explanations and figures, to show how to map the various associations in Nhibernate. Read through Articles 1- 5 since they have the code samples embeded in figures in the article with explanations.
INTRODUCTION
For object relational mapping in a rapid application development setup using C#, the default choice is Language Integrated Query and the other main option is NHibernate. There are scenarios where NHibernate is a useful addition. Both SPRING.NET and CASTLE WINDSOR, two of the oldest DI containers, have extensive support for NHibernate. There is zero leakage of concerns when NHibernate is used correctly for persistence. If coded correctly, all POCOs (PLAIN OLD CLR OBJECTS) are guaranteed to be free from persistence and transaction concerns. This adds huge advantages for domain classes reuse, unit testing and test driven development. For strategic purposes, resource managers and pocos inclined architects, NHibernate offers other unique advantages they are very well aware of and needs no introductions from me. That said, unless there is a strategic or systems requirements demanding NHibernate usage, Language Integrated Query should be the chosen path in C#. But if a requirement for NHibernate exists & you are new to ORM and NHibernate, then this 8 part article series may be useful to you.
DESCRIPTION
Hopefully you have read the article given in Pre-Requisites section posted on the official NHibernate site blogs and tried it because its the best introduction to NHibernate.
The fundamental starting point in developing and consuming ORM software is fixing the identity of an object. Object Identity is important for all ORM systems including NHibernate because when a CLR loaded object is modified, an ORM software must know without ambiguity to which particular row in the database table the changes must be made persistent by using DML SQL statements which are generated automatically for every unit of action performed in the object (object creation & save=insert statement, object modification = update statement, object deletion = delete statement). In other words, ORM software should know, to what row in a database table the object maps to without ambiguity. (Though this statement is more applicable to object-relation mappings using Row Gateway pattern, we will not get into that discussion here.) So every object will have a separate identity assigned to it, which is stored as a property in it and mapped in a NHibernate mapping file using the keyword <id>. Astute developers would immediately jump to the conclusion that this identifier must be related to PRIMARY KEY because I said "unambiguously identify a single row in a database table". Yes you are right and this identifier property in every class is essentially its primary key in the database table and it is best to keep it as a synthetic or surrogate key.
Please refer to the example FIGURE I below which shows a C# class called Customer having a property called CustomerId of type long mapped using the NHibernate mapping file Customer.hbm to a Database Table called CUSTOMER TABLE with the same CustomerId property of the C# class becoming a primary key of the table.
The GREEN ARROW SHOWS THE MAPPING OF C# CLASS TO .HBM MAPPING FILE AND HOW THE IDENTIFIER PROPERTY IN the C# CLASS IS DEFINED IN the .HBM MAPPING FILE USING THE <ID> TAG TO DENOTE THAT IT'S NOT A NORMAL PROPERTY BUT AN OBJECT IDENTIFIER IN the C# WORLD AND WILL SERVE AS A PRIMARY KEY FOR A TABLE IN the DATABASE WORLD.
THE ORANGE ARROW INDICATES HOW THE TABLE IS FORMED WITH A PRIMARY KEY SET EXACTLY AS INDICATED BY THE <ID> TAG OF the NHIBERNATE MAPPING FILE. HERE IN THIS EXAMPLE IT IS "CUSTOMERID".
FIGURE I
As said earlier, fixing the object identity and mapping it to be the primary key of the database table is the first step in any ORM. So now the question is "Do we have to add the Id property to all classes by default?" . To know the answer for this, one must know the distinction between Entity and Valuetypes in NHibernate.
Entity and Value Types
The distinction between Entity and Valuetypes is well understood by an example. Consider the example of an ECommerce site which we use for all articles here. Naturally every ecommerce site stores the information about the buyer and hence has a Customer class which may include a field for EMailAddress. Hence the Customer class will need to include in its behaviour a constraint check for valid emailaddress entry and additional methods to send automated mails (there is always plenty of these mails sent by ecommerce sites to customers). As an outcome of all this behavioural additions caused by an EMailAddress field, the Customer object loses cohesion. Hence the EMailAddress field must be made a separate class with all this behavior moved to the EMail class. But we cannot represent EMail as a separate database table because a one to one link exists between Customer and Email and a one to one link definitely throws a hint to a modeler that both tables may need to be unified to a more appropriate entity to which a database table must be created. Also our EMail class includes a lot of behaviour (functinaolity to check email address format, send email and perhaps even use email address for login id) but still has only one field, emailaddress, further confirming that this one to one link needs to be unified into a single entity. Also, while the EMail class improves cohesion in the object model, it is found that an EMail object cannot exist independently without a Customer object and is always associated with one Customer object without SHARED REFERENCES because neither can an email be shared between two customers nor there is any use in email of a customer if the customer ceases to exist. Objects like Customer which have independent lifetimes are termed Entity and dependent objects like EMail which cannot exist independently and have no shared references (strictly one to one associations only) are termed Valuetypes in NHibernate.
Entity objects have database tables and hence require an identifier property in a class and corresponding id attribute when they are mapped.
Valuetypes do not have a separate table and are placed in the same table as the entity to which they are tightly related in a ONE to ONE relationship like a Customer entity for the Email valuetype. Hence valuetypes do not need a seperate identifier field as they are identified by the entity with which they are linked and do not have a separate database table.
Since the lifecycle of Valuetype (ex. Email) is fully dependent on its related Entity (ex. Customer) without shared references, in UML models the association between Entity and Valuetype is COMPOSITION and not AGGREGATION. This association difference in the model may not change the code for Java and C# but in C++ this difference has a major change in the code because it means objects will have to be instantiated by value without pointers or references for Valuetypes.
LASTLY, IN NHIBERNATE, WHILE ALL ENTITY CLASSES HAVE A SEPARATE .HBM MAPPING FILE, VALUETYPES DO NOT HAVE A SEPARATE MAPPING FILE OR DB TABLE. THEY ARE PLACED WITH A <COMPONENT> TAG ON THE SAME HBM MAPPING File OF THE CLASS WITH WHICH THEY HAVE A ONE-TO-ONE ASSOCIATION. SO IN OUR EXAMPLE, CUSTOMER AND EMAIL CLASSES WILL HAVE THE SAME MAPPING FILE i.e. CUSTOMER. The HBM MAPPING FILE AND EMAIL WILL BE PLACED AS A <component> in that MAPPING FILE. WE WILL SEE HOW THIS IS DONE QUITE CLEARLY IN the "EXAMPLE FOR ENTITY AND VALUETYPES" SECTION NEXT.
Identifying Entity and Valuetypes
To identify an Entity, see if it satisfies the following checklist:
- Do the objects of this class have an independent lifetime? Yes for entity.
- Do the objects of this class have its references shared? Maybe Yes for entity.
- Do the objects of this class need a separate Database Table and identifier? Yes.
To identify a Value Type it is A DEFINITVE NO for all the questions in the check list.
Example for Entity and Valuetypes
In our sample ecommerce scenario application, there is a Customer C# class (with id property) and Email C# class (without id property) mapped to one and the same table "CUSTOMER" in the mapping file Customer.hbm.xml. We write the C# code for Customer and Email class as always and create a mapping file for the Customer class by adding a Customer.hbm.xml file in Visual Studio. The Customer.hbm mapping is as follows,
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly ="EShopSystem" namespace ="EShopSystem.Domain" >
<class name="Customer" table="CUSTOMER" >
<id name ="CustomerId" column ="CUSTOMERID" type ="long" generator="native" />
<property name="CustomerName" column="CUSTOMERNAME" type="string" />
<component class="Email" name="EmailIdentity">
<property name="EmailAddress" column="EMAILADDRESS" type="string" />
</component>
</class>
</hibernate-mapping>
Please refer to the FIGURE II below. In this figure, I show how the preceding mapping file corresponds to C# classes. I have put the C# class codes of Customer and Email classes on the top row of the figure, side by side, separated by a black bar. Following the C# code for Customer and Email class, I have put the Customer.hbm mapping file code. By using arrows I show how the mapping is done between C# code classes and their corresponding .hbm mapping files. People who do not like this style of representation may just download the sample project in Article 7 of the series and refer to the C# code and mapping files. But this is a far simple approach to understand NHibernate mappings.
Now to the explanation of the simple code in figure II below. First, before proceeding, further check the C# code in the figure II below and observe that the Customer Entity class has a property of type long called CustomerId to store the object identifier which is absent in the valuetype Email because, as already explained earlier, the entity customer needs the identifier, since it has its own database table - CUSTOMER TABLE and every row in it corresponds to a C# object of type Customer and the primary key of each row is stored in the identifier property in its corresponding C# object. A Valuetype like Email does not have their own database table and hence do not have this identifier. Now let's proceed with the explanation of the Figure II below.
The Customer class being an entity, must have a table - CUSTOMER and a corresponding Customer.hbm mapping file. Follow the Green arrow for the mapping of the Customer C# class to its corresponding <class> tag in the Customer.hbm mapping file.
As explained previously, the Email class is a valuetype. So it cannot have a table or a mapping file of its own. So EMail is mapped in the Customer class .hbm mapping file itself by using a <component> tag as shown below in the figure II. Follow the Orange arrow from Email class C# code to its corresponding <component> tag in the same Customer.hbm mapping file.
The association between customer and email is captured in C# in the ususal manner of storing a reference of EMail object in the Customer named EmailIdentity property in the Customer C# class. Follow the purple arrow in the Figure II to see the mapping of the property EmailIdentity in the Customer class to its corresponding tag in the mapping file.
The end result of this <component> mapping for the Email class in the Customer class mapping file is that in the database, the CUSTOMER table will now carry columns for the fields of the Email class also. Finally, Figure II also shows the snapshot of the Database Table CUSTOMER which includes the fields of the Email Valuetype also (shown with a triangle in Figure II).
FIGURE II
So more classes than tables can be used which makes it possible to have a rich domain model mapped to fewer tables by mapping the classes as entity and valuetypes appropriately using the checklist given previously.
One to One Entity Association
Earlier it was found that there is a One to One association between Customer and Email but when we analyzed this One to One association more thoroughly it was found that EMail is a value type to be put in the same table as Customer and hence it is not a One To One entity association mapped to separate tables. Now the question is how to map a One to One Association of Entity types.
Associations are created between objects by storing references. They may be unidirectional or bidirectional. For example, to create an association between object B & object A, we store the reference of B in A as shown in part 1 of Figure III below. But databases do not have the concept of references. Hence in databases, to establish a link from Table A to Table B, the primary key of Table B is made as Foreign Key in Table A. Now navigability is established between them. See Part 2 of Figure III. All ORM software maps one-to-one associations to database tables using foreign keys from one table to the other. While this is easily deducible for one and all, let us not forget that this detail has to be put in mapping files of the entities involved in association for NHibernate to manage this. Two separate mapping files have to be provided corresponding to each of the classes involved in the association.
FIGURE - III
Coding One to One Mapping In NHibernate
Consider the scenario of an ECommerce application.
"The customer submits his order and makes a payment. The ecommerce application processes the order and once payment is approved, adds the order to the payment approved orders. "
The association between PaymentApprovedOrder and Payment is a one-to-one entity association in this scenaio. Without ploughing into the details of the fields and the behaviour of the classes PaymentApprovedOrder and Payment, let us look at the One-to-One association between these two entities which is more relevant to the discussion here.
Before going into the details of association, it's noteworthy to understand why the Payment class must be an Entity and not ValueType because it looks like its lifecycle is tied to the Order class and without an Order instance a Payment cannot exist. While it is true that a Payment cannot exist without an order, the life of a Payment instance extends beyond more than an Order instance because an order is fulfilled upon shipment but a Payment object may live beyond an Order and Shipment instance, especially if it requires further processing time for its fulfilment. A payment for an order could even extend for years if paid in parts. If you make Payment a Valuetype, then its lifetime will be over with an instance of an Order and cannot have persistence independently without an Order instance which is a problem. Hence Payment has to be made an entity to give it an independent lifetime. Just refer to the checklist for Entity and Valuetypes if still in doubt. NHibernate is all about making logical design decisions with strong object-oriented and relational analysis.
The figure IV below shows how the association between Payment and PaymentApprovedOrder can be mapped. For both the Payment and PaymentApprovedOrder classes, we add minimum properties, as our subject of interest here is the bidirectional one-to-one association between them and how it is mapped in NHibernate.
In the figure IV below, first the C# code for PaymentApprovedOrder and Payment is written. Following the C# code, the .HBM Mapping files are written. First to represent the association between PaymentApprovedOrder and Payment in C# code, references are stored as usual. See the C# code in the figure for PaymentApprovedOrder and Payment. These references will have to be mapped in the .HBM file to map the association in NHibernate. Now let's proceed to the explaination of Figure IV below to see how this association is mapped.
Let us first see how PaymentApprovedOrder is mapped. The class PaymentApprovedOrder stores a reference of Payment class instance named as OrderPayment. Look at the Orange arrow in figure IV to see how the stored Payment reference is mapped from C# class to its .HBM file.
This association is mapped as below.
In PaymentApprovedOrder. cs having the PaymentApprovedOrder C# class:
public virtual Payment OrderPayment { get; set; }
In the PaymentApprovedOrder.hbm file:
<many-to-one name ="OrderPayment" lazy="false" class ="Payment" column="PAYMENTID" unique="true" not-null="true" cascade="save-update"/>
(The lazy="false attribute is explained at the end of this article LAZILY. :-) )
The notable thing here is we said PaymentApprovedOrder has a one-to-one association with the Payment class. WHY IS IT MARKED WITH <MANY TO ONE> and not <ONE TO ONE>? Let us research this <many-to-one> tag first.
By this <many-to-one> tag here we specify that MANY-TO-ONE association exists between MANY instances of PaymentApprovedOrder to ONE instance of PAYMENT.
THIS USAGE SEEMS LIKE A CLEAR VIOLATION OF ONE-TO-ONE ASSOCIATION THAT IS REQUIRED BETWEEN PAYMENTAPPROVEDORDER AND PAYMENT.
As we already explained, an association between instances is represented in the database by a foreignkey relationship. So for mapping this <many-to-one> relationship between many instances of PaymentApprovedOrder to one Payment instance, to that in the database between PAYMENTAPPROVEDORDER table and PAYMENT table, the same foreign key of PAYMENT table must be repeated for many rows of the PAYMENTAPPROVEDORDER table (because it's a <many-to -one> and we cannot store collections in the column, so we can only repeat multiple times).
This is not what we want. We want each and every row in the PAYMENTAPPROVEDORDER table that represents a separate object of the PaymentApprovedOrder class to be posted with a foreign key from the PAYMENT Table that represents exactly one instance of the Payment object SUCH THAT EACH OF THE FOREIGN KEYs FROM the PAYMENT TABLE OCCURS ONLY ONCE IN the PAYMENTAPPROVEDORDER TABLE (because it is <one-to-one>).
If you look at the attributes for this <many-to-one> it says UNIQUE=TRUE. WHAT THIS MEANS IS THAT THE FOREIGNKEY WHICH IS GOING TO BE POSTED FROM the PAYMENT TABLE TO the PAYMENT APPROVEDORDER TABLE WILL BE UNIQUE FOR THAT COLUMN WITHOUT REPEATS. BECAUSE OF THIS UNIQUE ATTRIBUTE, A FOREIGN KEY POSTED FROM the PAYMENT table WILL OCCUR ONLY ONCE IN the PAYMENTAPPROVEDORDER table WITHOUT REPEATS thereby satisfying a ONE-TO-ONE ASSOCIATION.
Now the question in all the minds reading this would be: Why not directly use a <one-to-one>? A <one-to-one> association has a different purpose which we will see next.
The other question would be, are we not acheiving the functionality of a <one-to-one> association crudely by using <many-to-one> and constraining it with a unique attribute? Doesn't this look like some kind of hack? The answer is certainly not.
We will look further at this.
We need to understand how NHibernate looks at a mapping file and inteprets an association defined by us as <many-to-one> or <one-to-one> or <many-to-many>. Every association has two ends. Be it <one-to-one> or <many-to-one> or <many-to-many>, every link has two ends to it. In NHibernate when we specify <one-to-one> or <many-to-many> or <many-to-one> etc, WE MAP ONLY ONE END OF THIS LINK AND THAT END IS THE TARGET OF THE DEFINITION i.e <....-to-TARGET END>. So cheekily saying it, you could as well say in the NHibernate mapping file, <something-to-one> or <something-to-many> . NHibernate always operates on only the target end of the link when an association is defined in a mapping file. Hence FOR A BIDIRECTIONAL ASSOCIATION, we need to map both ends of a link in two separate mapping files for each end of the link. Now in our earlier mapping when we used the <many-to-one> mapping, we were actually operating on the <......-to-one> end only i.e the Payment end only. So when we constrained it with the "UNIQUE" attribute, we actually ensured that the foreign key from the target end table (i.e the PAYMENT table) will be unique without repeats, thereby ensuring that the association becomes <one-to-one>.
Now what do we do if we want this to be a bidirectional link?
We need to also map the other end of the association. To do this we use the Payment.hbm file.
Now let's look at how the mapping is done. Please refer to Figure IV. Look at the green arrow to see how the stored PaymentApprovedOrder reference is mapped from C# code to its mapping file.
The code below shows a snippet from Payment.cs and Payment.hbm to show the bidirectional link.
In the Payment.CS file having the Payment class:
public virtual PaymentApprovedOrder PaidOrder { get; set; }
In the Payment.hbm file:
<one-to-one name ="PaidOrder" lazy="false" class ="PaymentApprovedOrder" property-ref="OrderPayment"/>
In the mapping file for Payment, we see that the other end of the association is defined exactly the way we like to see, a <one-to-one> mapping. Look at the purple arrow to see how attributes of this mapping is set. Especially one particular attribute called "property-ref". The <One-to-One> mapping has an attribute called "property-ref". To this "property-ref" attribute we set the reference of the other side of the link i.e the instance "OrderPayment" to signify that we have already mapped that end in the other mapping file and this end is the inverse end of it. NHibernate will know how to manage it.
FIGURE IV
Now we have completed the One-to-One association. Similiarly we will complete <many-to-one> and <many-to-many>, optional associations, jointables and collections of valuetypes in this 8 part article series.
The "lazy" attribute
For clarity, I have removed the attribute "lazy=false" from figure IV but you will see it in the code snippets presented from mapping files. What is this "lazy" attribute? Briefly, an object-oriented system consists of a web of objects interconnected by associations. All these objects are made persistent to the database. So when we need to fetch information for an object from the database, we may end up bringing down an avalanche of objects because the object we need, will have associations or collections which in turn may have other associations and everything has to be fetched from the database just to fully fetch one single object. Something worse is in the end we may not use any of the associations or collections on the object we fetched. To avoid this avalanche, by default NHibernate uses "lazy=true", to lazily fetch all the associations and collections of the object at a later time if needed and to currently fetch only the object immediately in use, in which case only the object is returned with proxies or placeholders for its collections and many associations. In our case we know for sure we are not going to cause an avalanche of fetches from the database, so we say lazy=false. Note that lazy=false is convenient but the best option always in a real-world scenario is lazy=true.
But Setting "lazy=true" here in the sample application will cause an exception. Why? I will tell you the problem. We have a generic convenience class called DBRepository<...> where we open a Session object to NHibernate and do some operations with the database and close the session object at the end. For example, when we want to retrieve an object by identifier, we use the DBRepository<...>.getItemById(...). If lazy=true, the object is retrieved with proxies or placeholders for associations and collections and returned to the calling method when this DBRepository<...>.getItemById(..) is called. But if you look at DBRepository<....>.getItembyID(...), we close the session object at the end of the method call before exiting. So when the object retrieved from the database using DBRepository<...>.getItembyID(...) is consumed using one of its associations or collections, since the lazy initialization feature is on (because the lazy=true attribute is set) it will only have proxies for its associations and collections and hence will require the session object to go and fetch the information from the database. Unfortunately the session object is already closed and hence an exception is thrown. So the sample must be rearchitected for production use. In this eight part series we will also show how this can be done.
The sample Microsoft Visual Studio 2012 project in Article 7 of this 8 part series shows these association mappings. You may or may not need to change the ConnectionString. The client project in the code contains the sample code required to see each of the associations and mappings separately . All samples use POCOS. So they are fairly easy to comprehend. Articles 1-5 will have the full sample download of Article 7 embedded in them.
Let's enjoy coding with NHibernate.
REQUIREMENTS TO RUN THE SAMPLE CODE
Note that I am using the Microsoft Visual Studio 2012 TRIAL VERSION and the SQL database provided with it. While downloading and running the sample provided in Article 7, make appropriate changes to the connection string and others, as required. Articles 1-5 will have the sample code from that download project in Article 7 embedded in each article itself.
SUGGESTIONS
Expert users of NHibernate and developers with a good idea of using Hibernate may offer their suggestions on how best to improve the NHibernate mappings presented here or to incorporate corrections to the concepts discussed here by mailing to [email protected]. Won't reply but will definitely read them.