Basics: "1.0" "Utf-8" "Urn:nhibernate-Configuration-2.2" "Dialect"
Basics: "1.0" "Utf-8" "Urn:nhibernate-Configuration-2.2" "Dialect"
Basics: "1.0" "Utf-8" "Urn:nhibernate-Configuration-2.2" "Dialect"
Configuration
Example Configuration:
ISessionFactory
Contains:
o Configuration (Mappings/Dialect/ConnectionString)
Most expensive to create, should be created once only at the start of the application.
Singleton
Thread Safe – Multiple threads can use the same ISessionFactory without fear of locking or
concurrency issues.
Exception Safe – If an exception is thrown it is handled internally and will keep working.
To create a session factory we first load a configuration object, then use BuildSessionFactory() to
return one.
NHibernate allows you to generate your DB scheme from your mapping files.
The first argument to the SchemaExport.Create() method determines whether the SQL to be
executed to the Database is outputted to the Visual Studio output window.
The second argument determines whether the script is executed against the Database.
Session
Cheap to create
NOT thread safe
NOT exception safe
Unit of Work – guarantees a row will be represented by a single instance.
o Allows lazy loading, change tracking and persistence
Contains first level cache
o Is an identity map, guarantees that only one instance of one entity is loaded in the
context of a session.
o Not really a proper cache.
In the context of a web application ONE session should be created and disposed of for ONE
request. Session per method is VERY VERY BAD as you NHibernate is unable to cache
effectively and you will create multiple expensive DB connections.
Transaction
Transactions are MANDATORY
A common mistake when using a database is to use transactions only when orchestrating
several write statements. In reality, every operation that the database is doing is done inside
a transaction, including queries and writes (update, insert, delete).
When we don't define our own transactions, it falls back into implicit transaction mode,
where every statement to the database runs in its own transaction, resulting in a large
performance cost (database time to build and tear down transactions), and reduced
consistency.
Not using a transaction means using a second level cache will not work properly either.
See http://nhprof.com/Learn/Alerts/DoNotUseImplicitTransactions for more details.
Session.Load<T>(id)
o This is a lazy operation and will not actually hit the database. This is useful if you
need an entity purely as a reference for saving another object.
o Will always be a session cached entity
Session.Get<T>(id)
o This is not lazy and will load all the properties of an entity, unless a property has
specifically been set to lazy
o Will attempt to load from the session cache if it exists, otherwise will hit the
database and put it in the session level cache
Session.Query()
o Could be linq, criteria or hql (explained more later)
o This will always go to the database, but will put things into the session level cache.
Load and Get are the most efficient ways to select by ID. NEVER use a query for this.
If you use different methods in the context of a session to load the same entity you will
always have the same object reference to the entity.
var carName = car.Name; // will load the all the entity's properties
var car2 = session.Get<Car>(1); // this will not hit the db as it has already been
loaded previously by accessing the Name property
Mappings
All mappings need three things
Assembly
Namespace
o If you use a namespace then you don’t need a fully qualified name in the class tag.
Class
Id
ID
Guid
o Disadvantages
Not sequential so is slower to index
Large memory footprint
Has no meaning for a user
Assigned
o More for legacy systems when adapting nhibernate
Identity
o Good but requires an additional read to insert to the database (select scalar)
o No batch inserts are possible
Composite ID
o Uses a class to represent the identity
o Must implement equals() and getHashCode()
o Not recommended
High / Low
o NHibernate manages the ID generation
o Creates a table to manage ranges. So when the session factory is loaded it requests
a range of ids. By default this is 16,000. For the life of that session factory’’s
existence it will use these ids for adding objects.
o ScemaExport will create this table
o Farm safe
o You can create multiple hilo tables per class if required
o A drawback is if you need to insert objects manually with SQL you have to replicate
the range fetching algorithm within SQL (normally as a user function)
<id name="Id">
<generator class="hilo">
<!-- Optional paramaters for specifying which table/column to use -->
<param name="table">hibernate_unique_key</param>
<param name="column">classB_nexthi</param>
<param name="max_lo">20</param>
</generator>
</id>
Properties
Use a property to map a value type on your domain model, to a column or calculated property in the
database
<property name="PropertyName"
column="column_name"
type="typename"
update="true|false"
insert="true|false"
formula="arbitary sql expression"
access="field|property|ClassName"
optimistic-lock="true|false"
generated="never|insert|always"
/>
Use to map and association between two classes, in relational terms this to associate an entity
through a foreign key relationship.
<many-to-one name="PropertyName"
column="column_name"
class="ClassName"
cascade="all|none|save-update|delete"
fetch="join|select"
update="true|false"
insert="true|false"
property-ref="PropertyNameFromAssociatedClass"
access="field|property|nosetter|ClassName"
unique="true|false"
optimistic-lock="true|false"
not-found="ignore|exception"
/>
Name – Name of the property in the class
Column – Name of the column with the id for this
o Not required if same name as property
o SchemaExport will create
Class – Class of the associated class
o Not required, NHibernate will discern from class
Cascade
o Not required, none is default
o By setting this value, we are able to tell NHibernate to automatically traverse an
entity's associations, and act according to the cascade option. For instance, adding
an unsaved entity to a collection with save-update cascade will cause it to be saved
along with its parent object, without any need for explicit instructions on our side.
none - do not do any cascades, let the users handles them by themselves.
save-update - when the object is saved/updated, check the associations and
save/update any object that require it (including save/update the
associations in many-to-many scenario).
delete - when the object is deleted, delete all the objects in the association.
delete-orphan - when the object is deleted, delete all the objects in the
association. In addition to that, when an object is removed from the
association and not associated with another object (orphaned), also delete
it.
all - when an object is save/update/delete, check the associations and
save/update/delete all the objects found.
all-delete-orphan - when an object is save/update/delete, check the
associations and save/update/delete all the objects found. In additional to
that, when an object is removed from the association and not associated
with another object (orphaned), also delete it.
Fetch
o Not required, default is select
o Do you do a join or sub-select when eager-loading this association
o Only works when lazy is set to false
Update
o Not required, default is true
o Do I update the column when I update the entity
o Even if you change the association it will not be saved
Insert
o The same except when adding the entity for the first time
Property-Ref
o Not required, default is none
o Link to a property on the assocated class if not using the ID of the associated class
o Avoid using it
Access
Unique
o unique is relevant only if you use NHibernate to specify your schema. This would
generate a unique constraint when we generate the DDL.
Optimistic lock
o For caching
Not-found
o Not required, default is ignore
o For legacy data, where no FK constraint. Return null or throw exception
Collections
Set
o A collection of unordered unique entities
o For smaller collections.
o When you add an item to the set, it will retrieve all the elements in the set first to
ensure uniqueness.
o Will throw exception if you try to insert a duplicate.
o ISet
o .Net natively has no way Set Collection, so NHibernate uses the ISet implementation
from Iesi.Collections
o To instantiate: HashSet<T>();
o Because of the structure of an ISet, NHibernate doesn't ever UPDATE a row when
an element is "changed". Changes to an ISet always work via INSERT and DELETE
(of individual rows). Once again, this consideration does not apply to one to many
associations.
o
<set
name="PropertyName"
table="TableName"
schema="SchemaName"
lazy="true|false"
inverse ="true|false"
cascade="all|none|save-update|delete|all-delete-orphan"
sort="unsorted|natural|comparatorClass"
order-by="ColumnName asc|desc"
where="arbitrary sql where condition"
fetch="select|join"
batch-size="N"
access="field|property|className"
optimistic-lock="true|false"
outer-join="true|false">
<key />
<one-to-many/>
<many-to-many/>
</set>
Name - Property Name – required
Table - Table Name – required if different to the property name
Schema – If you need an prefix to your table. Eg: Dbo.Users
Lazy – Default true. If set to false will eagerly load the collection
o If false then set Fetch to join otherwise it will generate a separate query.
Inverse – Default false
o If inverse is set to true, it means it is not concerned with persisting transient
instance. It means the association at the other end of the association is concerned
with persistence.
o Used mostly with Many-to-many relationships but can also be used with one-to-
many relationships.
Cascade
o Not required, none is default
o By setting this value, we are able to tell NHibernate to automatically traverse an
entity's associations, and act according to the cascade option. For instance, adding
an unsaved entity to a collection with save-update cascade will cause it to be saved
along with its parent object, without any need for explicit instructions on our side.
none - do not do any cascades, let the users handles them by themselves.
save-update - when the object is saved/updated, check the associations and
save/update any object that require it (including save/update the
associations in many-to-many scenario).
delete - when the object is deleted, delete all the objects in the association.
delete-orphan - when the object is deleted, delete all the objects in the
association. In addition to that, when an object is removed from the
association and not associated with another object (orphaned), also delete
it.
all - when an object is save/update/delete, check the associations and
save/update/delete all the objects found.
all-delete-orphan - when an object is save/update/delete, check the
associations and save/update/delete all the objects found. In additional to
that, when an object is removed from the association and not associated
with another object (orphaned), also delete it.
Sort
o Dont use
Fetch
o Not required Default is select
o Enables join when retrieving the collection, only happens when lazy is set to false
batch-size
o Default is one
o Performance optimization
o Eg If you select 8 different entities one after another, and you set the batch-size to 5,
5 of the the entities will occur in one DB query, and 3 in the next.
o Big performance increase when iteration through the collection and accessing.
o Only applies if lazy
Access
o Type of member to map to
Optimistic-Lock: see concurrency section
Outer-Join ?
Key
o Reference to the parent entity id
o Column=<Name of the FK column>
One-to-many or
Many-to-many
One-to-many
This is a one-to-many relationship. E.g. Another class has a foreign key back to you. And will
probably have a many to one association as well. You should ask yourself if you really need a one-to-
many association as well. Or if the Many-to-one will suffice. As queries will usually achieve the same
thing in a more efficient manner.
<set name="Cars">
<key column="Driver" />
<one-to-many class="Car"/>
</set>
This is the simplest setup for a one-to-many relationship. Relying on the default columns that
NHibernate picks.
Many-to-many
Many-to-many associations have a mapping table. If you need any extra information on the mapping
association then use a concrete class instead.
So you definitely need a many-to-many association. You need to make some decisions to decide
which collection type to use
Unique
In most cases it will be unique so for this we should use a set
The problem with using sets is that they can get very large and this is bad for performance.
SO we use a combination of a bag and a set. The set will be on the side with fewer entities.
For example Users – Groups. As there will likely be more users than groups. The Group
mapping will have a bag of users, and the users mapping will have a set of groups.
The inverse goes on the BAG! This is because you want the set to manage the uniqueness.
Ordered
For an ordered many-to-many relationship you should use a List in place of the Set above.
Duplicates
If you need to allow duplicates, then use an IdBag
SchemaExport
This is where SchemaExport comes in very useful as it will set up the mapping table for you in the
correct format.
Example
Here is an example of a many-to-many unique relationship. Between Vehicles and Drivers.
IdBag
o An ordered collection of nonunique elements
o Allows you to map many-to-many associations and collections of values to a table
with a surrogate key
o The update performance of an <idbag> supersedes a regular <bag>. NHibernate
can locate individual rows efficiently and update or delete them individually, similar
to a list, map or set.
List
o An ordered collection of unique entities
o Requires a column, or will generate a column to represent position.
o Allows null values for position
o Do not expose the position on your domain model, Nhibernate must manage the
position.
o To instantiate: new List<T>();
Bag
o A collection of unordered nonunique elements
o No primary key
o Just adds.
o ICollection
o There is a particular case in which bags (and also lists) are much more performant
than sets. For a collection with inverse="true" can add elements to a bag or list
without needing to initialize (fetch) the bag elements! This is because IList.Add()
must always succeed for a bag or IList (unlike an ISet).
o To instantiate: new List<T>();
Map
o Collection of key value pairs
o IDictionary
o Instantiate new Dictionary<Tkey,TValue>();
Infrastructure
It is recommended that when using NHibernate the traditional three tiered approach to building
your application is not used. This discourages the use of respositories as the only area of session use,
using this hierarchy allows you to access the session within your UI where you can do UI specific data
tasks, for example paging or sorting.
DONT DO
UI Layer
Infrastructur UI Layer
Business e
Business Logic
Best Practice
One session per request
Worst Practice
Session per method, use one session per request.
Never use queries for select by ID, use get or load instead.
When creating new classes which need to be persisted, write the mappings first then use
SchemaUpdate to update the schema rather than use DotNet.Migrator
Change the core’s management of sessions to create and dispose of one in an IHTTPModule or an
ActionFilter
Allows session access in the Controller, return IQueryable from services or other classes and then
the UI can modify the results by adding paging and sorting logic.