Вы находитесь на странице: 1из 13

Basics

Configuration

 Configuration for NHibernate


 Requires Dialect: We specify the target DB platform
 ProxyFactory – We specify the assembly used to generate proxy classes required for lazy
loading. The default is Castle.DynamicProxy2
 ConnectionString – Connection to the DB, you can specify the name of the connection string
rather than the connectionstring itself so you can encrypt the string in the app.config.
 Assemblies where the Mappings are located.

Example Configuration:

<?xml version="1.0" encoding="utf-8" ?>


<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="dialect">
NHibernate.Dialect.MsSql2005Dialect</property>
<property
name="connection.provider">NHibernate.Connection.DriverConnectionProvider</propert
y>
<property name="connection.connection_string">
Server=(local)\SQLExpress;initial catalog=NH_MappingExercise2;Integrated
Security=SSPI
</property>
<property name='proxyfactory.factory_class'>
NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle
</property>
<mapping assembly="MappingExercise" />
</session-factory>
</hibernate-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.

var cfg = new Configuration().Configure("hibernate.cfg.xml");


var sessionFactory = cfg.BuildSessionFactory();
SchemaExport

NHibernate allows you to generate your DB scheme from your mapping files.

To do use we use the SchemaExport method on the Configuration class.

var cfg = new Configuration().Configure("hibernate.cfg.xml");


new SchemaExport(cfg).Create(true, true);
var sessionFactory = cfg.BuildSessionFactory();

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.

This is destructive so the DB will be dropped before the schema is created.

If you want to update the schema non destructively use SchemaUpdate.Execute()

var cfg = new Configuration().Configure("hibernate.cfg.xml");


new SchemaUpdate(cfg).Execute(true,true);
var sessionFactory = cfg.BuildSessionFactory();

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.

var session = sessionFactory.OpenSession()

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.

using (var transaction = session.BeginTransaction())


{
Console.WriteLine("Deleting a car");
var car = session.Get<Car>(1);
session.Delete(car);
transaction.Commit();
}

Getting Obects from the database


There are three ways to load objects from the database

 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 car = session.Load<Car>(1); // will not hit the db

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

var cars = session.CreateCriteria<Car>().List<Car>(); // will hit the db for every


car.
Lazyness
 Lazy Loading, allows you to have a reference to a property without actually loading it until it
is accessed.
 Default behaviour and in most cases the most efficient use, use eager loading only when
performance tuning.
 NHibernate will create a proxy class (using the configuration specified proxy factory) for a
lazy loaded property, which represents the property without loading it. Accessing any of the
properties fields will cause a query and the proxy to be replaced with the full object.
 The session.Load<T>() method will load a proxy object without hitting the Database, allows
you to use the object as a reference if you don’t actually need to know any of the objects
property values. If you want to eagerly load an object use session.Get<T>()
 If you use session.Load() then access a property it will cause the entire entity to be loaded
(all of its properties)
 Individual properties can be set to explicitly be lazy, such as a large content field. Then
Session.Get will not load that propery and it will only be loaded when accessed.
 None of this applies for associations

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

<?xml version="1.0" encoding="utf-8" ?>


<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="MappingExercise"
namespace="MappingExercise.Domain">
<class name="Car">
<id name="Id">
<generator class="native"/>
</id>
</class>
</hibernate-mapping>

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"
/>

 Name – The name of the property on the domain object


 Column – The name the db column the class mapped to
o Not required if the same name as the property
 Type – Nhibernate type, dotnet type, or your own type
o Not required will discern type from the class
 Update
o Not required default is true
o This is for read-only properties
 Insert
o Not required default is true
o For columns which have a default value in the DB
 Formula
o Not required
o Could do a count of children
 Access
o Not required default is property
o Is it a field or a property
o ClassName is your own custom access strategy
 Optimistic-Lock
o Not required default is false
o Is this property considered when doing optimistic lock
 Generated
o Not required. Default is never
o If we have a value which is generated in the database
Many-To-One

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

There are 5 different types of 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.

And the associated one-to-many on the other class is...

<many-to-one name="Driver" />

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

 It is unique, ordered or need to allow duplicates?

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.

In the Driver mapping.


<set name=”Vehicles” cascade=”all” table=”DriverVehicles”>
<key column="DriverId" />
<many-to-many class="Vehicle" column="VehicleId" />
</set>

In the Vehicle mapping


<bag name="Drivers" inverse="true" table="DriverVehicles">
<key column="VehicleId" />
<many-to-many class="Driver" column="DriverId"/>
</bag>

 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

Logic Sessions and


Transactions

Business Logic

Data Access NHibernate


Layer

Best Practice
One session per request

Use session.Get or Session.Load for selecting by ID.

Worst Practice
Session per method, use one session per request.

Never use queries for select by ID, use get or load instead.

Next Steps for the AMS

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.

Вам также может понравиться