Using NHibernate as an ORM Solution for .NET, Page 3
The main container for the class is the class tag:
<class name="ProductOrder, __Code" table="ProductOrder">
This tag defines the class name/assembly and the associated database table for the class.
Note: The assembly in this case is "__Code" because that is the default assembly for web sites. In many cases, the classes would be packaged up into a .dll file assembly that would be specified here, but to keep this sample application simple and accessible, I left the code in the default assembly.
The first item specified in the class tag definition is the ID tag:
<id name="ProductOrderID" column="ProductOrderID" type="Int32" unsaved-value="null"> <generator class="native" /> </id>
The ID tag is a required tag for NHibernate. Because you are persisting the object to a database, you need to identify each object as an individual record in the database. When designing a database, it is often best to have a unique non-business related identity column for each record to keep track of the record and also to use in any associations. Although composite identifiers consisting of one or more fields with business meaning (for instance, Order Number, SSN, and so on) make sense in some cases, they often lead to larger than needed foreign key fields and problems if the field should need to change for any reason. NHibernate does support composite identifiers, but recommends they are only used for working with legacy databases.
In this example, you use an integer identity column in the database to identify the record. The ID tag specifies the name of the object property and column name.
The generator tag specifies how this identity is generated for new objects being created. Most databases support some sort of automatically generating identity field. These identity fields often use either an automatically incrementing numeric field or sometimes a GUID. NHibernate works with these fields to generate the identity field for a newly saved object. In the example here, you use a MS SQL identity column for the ProductOrderID. In the generator tag, you specify "native," which defaults to the standard generator type for the current database (identity in this case). You could also directly specify "identity" or, if you want to use a sequence identifier for DB2/Oracle database, you can set up a "sequence" generator.
The remainder of the mapping file consists of basic property tags. For example:
<property name="ProductName" column="ProductName" type="String" />
Each of these property tags maps the property name to a database column. This is the general mapping type used for primitive types (short, int, long, decimal, string, and the like).
Person Class Mapping—Class Inheritance and Collections Example
The next mapping file for the Person class is a bit more complicated than the mapping file for the ProductOrder class. This example will show one of the methods of handling class inheritance and two methods for handling collections.
Take a look at the Person.hbm.xml mapping file:
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"> <class name="Person, __Code" table="Person"> <id name="PersonID" column="PersonID" type="Int32" unsaved-value="null"> <generator class="native" /> </id> <property name="FirstName" column="FirstName" type="String"/> <property name="LastName" column="LastName" type="String"/> <joined-subclass name="Customer, __Code" table="Customer"> <key column="PersonID"/> <property name="Address" column="Address" type="String"/> <property name="City" column="City" type="String"/> <property name="State" column="State" type="String"/> <property name="ZipCode" column="ZipCode" type="String"/> <set name="Orders"> <key column="CustomerID"/> <one-to-many class="ProductOrder, __Code"/> </set> </joined-subclass> <joined-subclass name="SalesPerson, __Code" table="SalesPerson"> <key column="PersonID"/> <property name="EmployeeNumber" column="EmployeeNumber" type="String"/> <set name="AssignedCustomers" table="AssignedCustomers" cascade="save-update"> <key column="SalesPersonID"/> <many-to-many class="Customer, __Code" column="CustomerID"/> </set> </joined-subclass> </class> </hibernate-mapping>
Listing 4: Sample Application—Person Mapping
The first thing that pops out at you when looking at this mapping is that the class mapping for the Person class includes the Customer and SalesPerson classes within it. This is because the Customer and SalesPerson classes inherit the Person class.
There are three ways to map class inheritance to a database using NHibernate:
- Table per class hierarchy
- One table with all of the properties for multiple subclasses
- Different classes are differentiated by a discriminator/type column
- Table per subclass
- One table for the parent class and one table for each subclass
- Table per concrete class (some limitations)
- One table for each subclass; each table includes shared parent class fields