You are on page 1of 17

ORM Approach for Database

Transactions

By

Puneet Bhatnagar
Contents

Introduction.......................................................3
Background........................................................3
Better Approach for Database Transactions.............4
Address Book.............................................................5
Installing NHibernate...................................................................................................5
The Mapping Files.......................................................................................................9
Integrating NHibernate..............................................................................................12
Configuring Nhibernate, Creating, Loading and Saving Objects..............................13
Note: These files does not make the complete application however I have just put
the things which make Nhibernate clear to users. To use these functions we have to
come up with our own logics, driven by the design of the project............................17

Conclusion.......................................................17
References.......................................................17
Introduction
This document addresses the database transaction issues, in terms of time a
developer spends in writing SQL queries to establish connection, read and save the
data to DB and rewriting the connection strings every time Database migration
happens, faced by all desktop and web applications and discusses ORM approach
that can be followed to resolve "persistence tier"—reading business object data from
a database and writing it back again. Hibernate/NHibernate is an object-relational
mapping (ORM) library for the Java/.NET languages, providing a framework for
mapping an object-oriented domain model to a traditional relational database.
AddressBook is taken as example so that problem resolving approaches can be
explained in detail.

Purpose
Its purpose is to relieve the developer from a significant amount of relational data
persistence-related programming tasks.

Background
Data management tasks in object-oriented (OO) programming are typically
implemented by manipulating objects, which are almost always non-scalar values.
For example, consider an address book entry which represents a single person along
with zero or more phone numbers and zero or more addresses. This could be
modeled in an object-oriented implementation by a "person object" with "slots" to
hold the data that comprise the entry: the person's name, a list (or array) of phone
numbers, and a list of addresses. The list of phone numbers would itself contain
"phone number objects" and so on. The address book entry is treated as a single
value by the programming language (it can be referenced by a single variable, for
instance).

However, many popular database products such as SQL DBMS can only store and
manipulate scalar values such as integers and strings organized within tables.
Various articles estimate that from one-quarter to one-third of application code in an
n-tier application is dedicated to the "persistence tier"—reading business object data
from a database and writing it back again. The code is repetitive, time-consuming,
and a chore to write.

The programmer must either convert the object values into groups of simpler values
for storage in the database (and convert them back upon retrieval), or only use
simple scalar values within the program. Object-relational mapping is used to
implement the first approach. It saves the programmer from writing that repetitive
code to persist the application information into the DB and saves a lot of time by
eliminating the conversion of an object to simpler scalar values and vice versa, since
it saves the information in the form of object itself in the Database.

The crux of the problem is translating those objects to forms which can be stored in
the database, and which can later be retrieved easily, while preserving the properties
of the objects and their relationships, these objects are then said to be persistent.

Better Approach for Database Transactions


Various solutions to this problem are available. Code generators can create data
access code in seconds. But if the business model changes, the code has to be re-
generated. "Object-relational managers" (ORMs) like Hibernate/NHibernate take a
different approach.

Hibernate/NHibernate is a Framework in itself. It loads business objects from a


database and saves changes from those objects back to the database. It can load or
save an entire object graph with just a line or two of code, this saving a lot of time of
developer.

Hibernate/NHibernate uses mapping files to load business objects from a database


and to save the changes back to database. As an alternative, you can use attributes
on classes and properties, instead of mapping files. However, mapping files make for
a cleaner separation between business logic and persistence code.

So, a developer needs to write only a few lines of code in an application and create
mapping files to save and retrieve data for each persistent class, and NHibernate
takes care of all database operations.

Note that Hibernate/NHibernate is not the only ORM framework in the coding
universe. There are literally dozens of commercial and open source products that
provide the same services. Hibernate/NHibernate is among the most popular one
Hibernate probably is very popular ORM Framework in the Java universe. In addition,
Microsoft has promised an 'Entity Framework' for ADO.NET, to provide ORM services.
AddressBook is taken as example so that scenario can be explained in detail.

Hibernate is a Professional Open Source project and a critical component of the JBoss
Enterprise Middleware System (JEMS) suite of products.
Here Core refers to Hibernate Core. Hibernate Core for Java generates SQL for you,
relieves you from manually writing sql queries. Hibernate adapts to your
development process, no matter if you start with a design from scratch or work with
an existing database, and it will support any application architecture. Combined with
Hibernate EntityManager and Hibernate Annotations you can use Hibernate as
a certified Java Persistence provider.

I will demonstrate the application of Hibernate using a .NET application; the same
concept is applicable to Java with language compatible changes.

Address Book
Consider a simple application like Address Book where user keeps information about his
friends, relatives, peers and all. Lets say we have two classes, which holds the Personal
and Official information for all people.

Installing NHibernate
The first step in using NHibernate is to download NHibernate and Log4Net, an open-
source logging application that NHibernate can use to record errors and warnings.
NHibernate contains the most recent Log4Net binary, or you can download the entire
Log4Net install package. To get started, you’ll need to download the framework at
http://nhibernate.sourceforge.net. Reference the assembly in your project. The next step
will be to add the appropriate configuration settings to your application’s config file to
tell NHibernate where and what your data store is:

/**
* App.config file created by Puneet Bhatnagar on 11/09/2008.
*/

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


<configuration>

<!-- Configuration Sections In This File -->


<configSections>
<!-- NHibernate Section -->
<section
name="hibernate-configuration"
type="NHibernate.Cfg.ConfigurationSectionHandler,NHibernate"
/>

<!-- Log4Net Section -->


<section
name="log4net"
type="log4net.Config.Log4NetConfigurationSectionHandler,
log4net"
/>

</configSections>

<!-- NHibernate Configuration -->


<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">
NHibernate.Connection.DriverConnectionProvider
</property>
<property name="dialect">
NHibernate.Dialect.MsSql2005Dialect
</property>
<!-- I am using SqlServer2005 as Database, so according to needs these
properties can be modified -->
<property name="connection.driver_class">
NHibernate.Driver.SqlClientDriver
</property>
<property name="connection.connection_string">
Data Source=WIN-PADADHIC\PUNEET;Initial
Catalog=AddressBook;Integrated Security=True;Pooling=False
</property>
<!-- Win-PADADHICH\PUNEET is the server name -->
<!--AddressBook is the name of Database -->
</session-factory>
</hibernate-configuration>

<!-- Log4Net Configuration -->


<log4net>

<!-- Define an output appender (where the logs can go) -->
<appender name="LogFileAppender"
type="log4net.Appender.FileAppender, log4net">
<param name="File" value="log.txt" />
<param name="AppendToFile" value="true" />
<layout type="log4net.Layout.PatternLayout, log4net">
<param name="ConversionPattern" value="%d [%t] %-5p %c [%x]
&lt;%X{auth}&gt; - %m%n" />
</layout>
</appender>

<!-- Note: Priority level can be


ALL/DEBUG/INFO/WARN/ERROR/FATAL/OFF -->
<!-- Setup the root category, set the default priority level and
add the appender(s) (where the logs will go) -->
<root>
<priority value="WARN" />
<appender-ref ref="LogFileAppender" />
</root>
</log4net>

</configuration>

Note: In case anyone wants to use Oracle DB he/she can simply modify one
property, assuming his DB is in same path and Hibernate will take care of the rest.

Going to next step the developer should be very clear about the Business Model and
Database Model. As told earlier, I am using Address Book as an example where I am
assuming that I have following persistent classes which are

♦ PersonalInfo
♦ OfficialInfo
Object Model:
AddressBook

OfficialInfo
PersonalInfo
One-To-One + Properties
+ Properties + UsedId
+ UserId + CompanyName
+ UserName + Designation
+ CallingName + DirectNumber
One-To-One
+ PhoneNumber + PANNumber
+ MobileNumber + PassportNumber
+ Address + EmailId
+ EmailId + PersonalInfo
+ OfficialInfo

Database Model:
Note that the database and the object model need not match perfectly. Every class
has to have a field on it containing data that uniquely identifies the instance on the
persistence table. There is a field with name Id. This special field is known as the Id
Property. When you map it, you have to provide the standard set of attributes, and
two special ones: the generator and the unsaved-value.

The <id> Tag

Once we have identified the class and its mapping table, we need to specify the
identity property of the class and its corresponding identity column in the mapping
table. In the column's IdentitySpecification property, we specified that the column
was the identity column, that it should initialize at 1 and increment by the same
value:

The selection of Id column is based on the design and it need not to be Id field for
that matter. Like in this example UserId could be the Identity column for the
identified class.

The Mapping Files


The next step is to provide the mapping files that fill our domain model from the data
tables. Mapping is the heart of what NHibernate does, and it presents the greatest
stumbling blocks for beginners.

Mapping simply specifies which tables in the database go with which classes in the
business model.

Each class requires its own mapping file, which can be stored wherever you like.
However according to the best practices we should keep the mapping tables with the
class itself so it becomes easy to modify.

The mapping files help Hibernate to connect classes and their persistent properties to
the database. Based on the design a class could have properties, which are not
persistent; in that case mapping files can just ignore the non-persistent ones. There
are couples of ways available to do mapping of the files like XML files, or by
attributes on classes etc. However the best-suggested way is XML files. For mapping
files the hbm.xml extension is the standard one in .NET. Continuing the example:

♦ Mapping file for PersonalInfo Class:

/**
* PersonalInfo.hbm.xml file created by Puneet Bhatnagar on 11/09/2008.
*/

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


<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="AddressBook"
assembly="AddressBook">

<class name ="PersonalInfo" table ="PersonalInfo" lazy ="false" >


<id name="Id">
<column name=" Id "/>
<generator class="native"/>
</id>
<property name="UserId" column= "UserId" type="String(50)" />
<property name="UserName" column= "UserName" type="String(50)" />
<property name="CallingName" column= "CallingName"
type="String(50)" />
<property name="PhoneNumber" column= "PhoneNumber"
type="String(50)" />
<property name="EmailId" column= "EmailId" type="String(50)" />
<property name="MobileNumber" column= "MobileNumber"
type="String(50)" />
<property name="Address" column = "Address" type="String(50)" />

</class>
</hibernate-mapping>

♦ Mapping file for OfficialInfo Class:


/**
* OfficialInfo.hbm.xml file created by Puneet Bhatnagar on 11/09/2008.
*/

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


<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="AddressBook"
assembly="AddressBook">
<class name ="OfficialInfo" table ="OfficialInfo" lazy ="false" >
<id name="Id">
<column name="Id"/>
<generator class ="native"/>
</id>
<property name="UserId" column= "UserId" type="String(50)" />
<property name ="CompanyName" column= "CompanyName"
type="String(50)" />
<property name ="Designation" column= "Designation"
type="String(50)" />
<property name ="DirectNumber" column= "DirectNumber"
type="String(50)" />
<property name ="EmailId" column= "EmailId" type="String(50)" />
<property name ="PANNumber" column= "PANNumber" type="String(50)" />
<property name ="PassportNumber" column= "PassportNumber"
type="String(50)" />
<property name ="Address" column= "Address" type="String(50)" />

</class>
</hibernate-mapping>

Based on the design of project the mapping could be:


♦ Many-to-one
♦ One-to-many
♦ Many-to-many

To make things more clear I am giving here the class definition for PersonalInfo
class.

using System;
using System.Collections.Generic;
using System.Text;

namespace AddressBook
{
public class PersonalInfo
{
//Property Variables
private int P_Id;
private string Personal_UserID;
private string P_UserName;
private string P_CalngName;
private string P_PhoneNo;
private string P_MobileNo;
private string P_EmailId;
private string P_Address;

//Properties
public virtual int Id
{
get { return P_Id ; }
set { P_Id = value; }
}
public virtual string UserId
{
get{ return Personal_UserID; }
set{ Personal_UserID = value; }
}
public virtual string UserName
{
get { return P_UserName; }
set { P_UserName = value; }
}
public virtual string CallingName
{
get { return P_CalngName; }
set { P_CalngName = value; }
}
public virtual string PhoneNumber
{
get { return P_PhoneNo; }
set { P_PhoneNo = value; }
}
public virtual string EmailId
{
get { return P_EmailId; }
set { P_EmailId = value; }
}
public virtual string MobileNumber
{
get { return P_MobileNo; }
set { P_MobileNo = value; }
}
public virtual string Address
{
get { return P_Address; }
set { P_Address = value; }
}

}
}

Likewise OfficialInfo class can also be defined. Going to next step.

Integrating NHibernate

In order to manipulate your persistent objects, you will have to complete the
configuration process. This means telling NHibernate which mapping files to load.
We can do this by pointing Nhibernate to the physical files, for this we need to keep
the track of the paths of the files. A better(the choice is all ours, which will be
dependant completely on our design) way is to let Nhibernate find the files as
embedded resources in assembly. For this set the “Build-Action” of each of the
mapping files to “Embedded Content”. Setting this property will tell the compiler to
add their images to assembly. Now what all we need to do is to tell the Nhibernate
which all classes are persistent in the application, and NHibernate will use the class
names to find the embedded mapping files that match them.

Persistent classes are actually managed through a Session object. NHibernate


provides a SessionFactory which builds the individual Sessions. Sessions are the
basic NHibernate unit of work. A session represents a conversation between your
application and NHibernate.

We can think of them as being one level up in a hierarchy from a transaction. A


session generally encompasses one transaction, but it can include several. Basically,
we open a NHibernate session, execute one or several transactions, close the
session, and dispose it.

Sessions are created by a SessionFactory object. A SessionFactory is resource


intensive and has a relatively high initialization cost. Sessions, on the other hand,
use limited resources and impose little initialization cost. So, the general approach is
to create a global SessionFactory when the application is initialized, and use that
SessionFactory to create session objects as needed.

Configuring Nhibernate, Creating, Loading and Saving Objects

Since the purpose of a Session is to provide a mechanism for managing the


performance of application, and the nature of that management is preventing
unnecessary round trips to the database, it stands to reason that operations on a
Session do not always result in direct operations against the underlying database. In
fact, it is the point of the Session to cache operations until they can be batched to
the database, thus saving round trips. All of which means that when we call a
persistent method on a Session, it may or may not result in immediate changes to
the database.

Usually, changes are only written to the database when the Session’s flush() method
is called. We can do this directly, of course, but it is more common to find that
flush() is being called on your behalf during some other operation, namely a
transactional commit.

One of NHibernate's strongest features is its ability to automatically cascade loads,


saves, and deletes. In other words, when we save an object, we save its entire
object graph. That feature dramatically simplifies our persistence code. I will explain
all these things in code by continuing my example of AddressBook.

Obviously, the AddressBook is not a complete, real world application, and good
design might call for a more fine-grained approach to the persistence tier. But
AddressBook effectively illustrates how NHibernate radically simplifies persistence
code.

The RegMgr class of AddressBook contains methods to implement all basic CRUD
(create retrieve, update, and delete) operations:
• Save(): This method saves a new or existing object to the database.
• RetrieveAll(): This method retrieves all objects of a given type from the
database.
• RetrieveEquals(): This method retrieves all objects of a given type where a
property of those objects equals a specified value. The method uses
NHibernate's QueryByCriteria feature, which can be implemented in a variety
of retrieval methods to retrieve 'like' string, or values within a specified
range.
• Delete(): This method has two overloads. The first deletes a single object
passed into it. The second deletes a list of objects passed into it.

/**
* RegMgr.cls file created by Puneet Bhatnagar on 11/09/2008. My
Namespace and Assembly names are same which is AddressBook.
*/

using System;
using System.Collections.Generic;
using System.Text;
using NHibernate;
using NHibernate.Cfg;
using System.Xml;
using System.Reflection;
using NHibernate.Expression;
using System.Data.SqlClient;
using System.Collections.Specialized;
[assembly: log4net.Config.XmlConfigurator(Watch = true)]
namespace AddressBook
{
public enum SessionAction { Begin, Continue, End, BeginAndEnd }
public class RegMgr:IDisposable
{

//Member Variables
private ISessionFactory factory;
private ISession session = null;

public RegMgr()
{
try
{

this.ConfigureLog4Net();
this.ConfigureNHibernate();
}
catch (Exception e)
{
Console.WriteLine(e.Message.ToString());
Console.WriteLine(e.InnerException.Message.ToString());
}
}

// The below given function is a Generic Function which can retrieve


//any type/object from DB, based on the parameter passed. In our case
//this single function can return us objects of PersonalInfo and
//OfficialInfo class.
public IList<T> RetrieveAll<T>(SessionAction sessionAction)
{

if ((sessionAction == SessionAction.Begin) ||
(sessionAction == SessionAction.BeginAndEnd))
{
session = factory.OpenSession();
}
ICriteria TargetObjects =
session.CreateCriteria(typeof(T));
IList<T> itemList = TargetObjects.List<T>();

if ((sessionAction == SessionAction.End) ||
(sessionAction == SessionAction.BeginAndEnd))
{
session.Close();
session.Dispose();
}
return itemList;

//This function can save any type/object into the database. In this
//case this single function can be used to save the objects of classes
//PersonalInfo and OfficialInfo into the DB.
public void SaveOrUpdateAll<T>(T item)
{
try
{
using (ISession session = factory.OpenSession())
{
using (session.BeginTransaction())
{
session.SaveOrUpdate(item);
session.Transaction.Commit();
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message.ToString());
Console.WriteLine(e.Source.ToString());
}
}

//This function again is a Generic function which can receive any


//type/object as parameter and then can delete that from DB.
public void Delete<T>(IList<T> itemsToDelete)
{
using (ISession session =
m_SessionFactory.OpenSession())
{
foreach (T item in itemsToDelete)
{
using (session.BeginTransaction())
{
session.Delete(item);
session.Transaction.Commit();
}
}
}
}

//Configuring Log4Net.
private void ConsfigureLog4Net()
{
log4net.Config.XmlConfigurator.Configure();
}

/// <summary>
/// Configures NHibernate and creates a member-level session
factory.
/// </summary>

//Configuring Nhinernate
private void ConfigureNHibernate()
{
// Initialize
try
{
Configuration cfg = new Configuration();
cfg.Configure();

/* Note: The AddClass() method requires that mappings


be contained in hbm.xml files whose BuildAction
properties are set to ‘Embedded Resource’. */

// Add class mappings to configuration object

cfg.AddCl
ass(typeof(AddressBook.PersonalInfo));
cfg.AddClass(typeof(AddressBook.OfficialInfo));

// Create session factory from configuration object


factory = cfg.BuildSessionFactory();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.Source);
}
}
}
}
Note: These files does not make the complete application however I have just put
the things which make Nhibernate clear to users. To use these functions we
have to come up with our own logics, driven by the design of the project.

Conclusion
This document discussed Nhibernate, an ORM approach for Database transactions.

References
The reference for creating this document is taken from below
http://sourceforge.net/projects/nhibernate/
http://www.Theserverside.net