The Spring Framework provides integration with NHibernate in terms of resource management, DAO implementation support, and transaction strategies. For example for NHibernate, there is first-class support with lots of IoC convenience features, addressing many typical NHibernate integration issues. All of these support packages for O/R (Object Relational) mappers comply with Spring's generic transaction and DAO exception hierarchies. There are usually two integration styles: either using Spring's DAO 'templates' or coding DAOs against the 'plain' NHibernate APIs. In both cases, DAOs can be configured through Dependency Injection and participate in Spring's resource and transaction management.
You can use Spring's support for NHibernate without needing ot use Spring IoC or transaction management functionality. The NHibernate support classes can be used in typical 3rd party library style. However, usage inside a Spring IoC container does provide additional benefits in terms of ease of configuration and deployment; as such, most examples in this section show configuration inside a Spring container.
Some of the benefits of using the Spring Framework to create your ORM DAOs include:
Ease of testing. Spring's IoC approach
makes it easy to swap the implementations and config locations of
Hibernate SessionFactory
instances,
ADO.NET DbProvider
instances,
transaction managers, and mapper object implementations (if needed).
This makes it much easier to isolate and test each piece of
persistence-related code in isolation.
Common data access exceptions. Spring can wrap exceptions from your O/R mapping tool of choice, converting them from proprietary exceptions to a common runtime DataAccessException hierarchy. You can still trap and handle exceptions anywhere you need to. Remember that ADO.NET exceptions (including DB specific dialects) are also converted to the same hierarchy, meaning that you can perform some operations with ADO.NET within a consistent programming model.
General resource management. Spring
application contexts can handle the location and configuration of
Hibernate ISessionFactory
instances,
ADO.NET DbProvider
instances and other
related resources. This makes these values easy to manage and change.
Spring offers efficient, easy and safe handling of persistence
resources. For example: related code using NHibernate generally needs
to use the same NHibernate Session
for
efficiency and proper transaction handling. Spring makes it easy to
transparently create and bind a Session
to the current thread, either by using an explicit 'template' wrapper
class at the code level or by exposing a current
Session
through the Hibernate
SessionFactory
(for DAOs based on plain
Hibernate 1.2 API). Thus Spring solves many of the issues that
repeatedly arise from typical NHibernate usage, for any transaction
environment (local or distributed).
Integrated transaction management. Spring allows you to wrap your O/R mapping code with either a declarative, AOP style method interceptor, or an explicit 'template' wrapper class at the code level. In either case, transaction semantics are handled for you, and proper transaction handling (rollback, etc) in case of exceptions is taken care of. As discussed below, you also get the benefit of being able to use and swap various transaction managers, without your Hibernate/ADO.NET related code being affected: for example, between local transactions and distributed, with the same full services (such as declarative transactions) available in both scenarios. As an additional benefit, ADO.NET-related code can fully integrate transactionally with the code you use to do O/R mapping. This is useful for data access that's not suitable for O/R mapping which still needs to share common transactions with ORM operations.
The NHibernate Northwind example in the Spring distribution shows a NHiberaten implementation of a persistence-technology agnostic DAO interfaces. (In the upcoming RC1 release the SpringAir example will demonstrate an ADO.NET and NHibernate based implementation of technology agnostic DAO interfaces.) The NHibernate Northwind example can serves as working sample app that illustrates the use of NHibernate in a Spring web application. It also leverages declarative transaction demarcation with different transaction strategies.
At the moment the only ORM supported in NHibernate, but others can be integrated with Spring (in as much as makes sense) to offer the same value proposition.
We will start with a coverage of NHibernate in a Spring environment, using it to demonstrate the approach that Spring takes towards integrating O/R mappers. This section will cover many issues in detail and show different variations of DAO implementations and transaction demarcations. Most of these patterns can be directly translated to all other supported O/R mapping tools.
The following discussion focuses on Hibernate 1.0.4, the major differences with NHibernate 1.2 being the ability to particpate in spring transaction/session management via the normal NHibernate API instead of the 'template' approach. Spring supports both NHibernate 1.0 and NHibernate 1.2 via seperate .dlls with the name internal namespace.
Typical business applications are often cluttered with repetitive
resource management code. Many projects try to invent their own
solutions for this issue, sometimes sacrificing proper handling of
failures for programming convenience. Spring advocates strikingly simple
solutions for proper resource handling, namely IoC via templating; for
example infrastructure classes with callback interfaces, or applying AOP
interceptors. The infrastructure cares for proper resource handling, and
for appropriate conversion of specific API exceptions to an common
infrastructure exception hierarchy. Spring introduces a DAO exception
hierarchy, applicable to any data access strategy. For direct ADO.NET,
the AdoTemplate
class mentioned in a previous
section cares for connection handling, and for proper conversion of
ADO.NET data access exceptions (not even singly rooted in .NET 1.1) to
Spring's DataAccessException
hierarchy, including
translation of database-specific SQL error codes to meaningful exception
classes. It supports both distributed and local transactions, via
respective Spring transaction managers.
Spring also offers Hibernate support, consisting of a
HibernateTemplate
analogous to
AdoTemplate
, a
HibernateInterceptor
, and a Hibernate transaction
manager. The major goal is to allow for clear application layering, with
any data access and transaction technology, and for loose coupling of
application objects. No more business service dependencies on the data
access or transaction strategy, no more hard-coded resource lookups, no
more hard-to-replace singletons, no more custom service registries. One
simple and consistent approach to wiring up application objects, keeping
them as reusable and free from container dependencies as possible. All
the individual data access features are usable on their own but
integrate nicely with Spring's application context concept, providing
XML-based configuration and cross-referencing of plain object instances
that don't need to be Spring-aware. In a typical Spring app, many
important objects are plain .NET objects: data access templates, data
access objects (that use the templates), transaction managers, business
services (that use the data access objects and transaction managers),
ASP.NET web pages (that use the business services),and so on.
To avoid tying application objects to hard-coded resource lookups,
Spring allows you to define resources like a
DbProvider
or a Hibernate
SessionFactory
as objects in an
application context. Application objects that need to access resources
just receive references to such pre-defined instances via object
references (the DAO definition in the next section illustrates this).
The following excerpt from an XML application context definition shows
how to set up Spring's ADO.NET DbProvider and a Hibernate
SessionFactory
on top of it:
<objects xmlns="http://www.springframework.net" xmlns:db="http://www.springframework.net/database"> <!-- Property placeholder configurer for database settings --> <object type="Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer, Spring.Core"> <property name="ConfigSections" value="databaseSettings"/> </object> <!-- Database and NHibernate Configuration --> <db:dbProvider id="DbProvider" provider="SqlServer-1.1" connectionString="Integrated Security=false; Data Source=(local);Integrated Security=true;Database=Northwin;User ID=springqa;Password=springqa;"/> <object id="MySessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate"> <property name="DbProvider" ref="DbProvider"/> <property name="MappingAssemblies"> <list> <value>Spring.Northwind.Dao.NHibernate</value> </list> </property> <property name="HibernateProperties"> <dictionary> <entry key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider"/> <entry key="hibernate.dialect" value="NHibernate.Dialect.MsSql2000Dialect"/> <entry key="hibernate.connection.driver_class" value="NHibernate.Driver.SqlClientDriver"/> </dictionary> </property> </object> </objects>
The basic programming model for templating looks as follows, for
methods that can be part of any custom data access object or business
service. There are no restrictions on the implementation of the
surrounding object at all, it just needs to provide a Hibernate
SessionFactory
. It can get the latter
from anywhere, but preferably as an object reference from a Spring IoC
container - via a simple SessionFactory
property setter. The following snippets show a DAO definition in a
Spring container, referencing the above defined
SessionFactory
, and an example for a DAO
method implementation.
<objects> <object id="CustomerDao" type="Spring.Northwind.Dao.NHibernate.HibernateCustomerDao, Spring.Northwind.Dao.NHibernate"> <property name="SessionFactory" ref="MySessionFactory"/> </object> </objects>
public class HibernateCustomerDao : ICustomerDao { private HibernateTemplate hibernateTemplate; public ISessionFactory SessionFactory { set { hibernateTemplate = new HibernateTemplate(value); } } public Customer SaveOrUpdate(Customer customer) { hibernateTemplate.SaveOrUpdate(customer); return customer; } }
The HibernateTemplate
class provides many
methods that mirror the methods exposed on the Hibernate
Session
interface, in addition to a
number of convenience methods such as the one shown above. If you need
access to the Session
to invoke methods
that are not exposed on the HibernateTemplate
,
you can always drop down to a callback-based approach like so.
public class HibernateCustomerDao : ICustomerDao { private HibernateTemplate hibernateTemplate; public ISessionFactory SessionFactory { set { hibernateTemplate = new HibernateTemplate(value); } } public Customer SaveOrUpdate(Customer customer) { return HibernateTemplate.Execute( delegate(ISession session) { // do whatever you want with the session.... session.SaveOrUpdate(customer); return customer; }) as Customer; } }
Using the anonymous delegate is particularly convenient when you would would otherwise be passing various method parameter calls to the interface based version of this callback. Futhermore, when using generics, you can avoid the typecast and write code like the following
IList<Supplier> suppliers = HibernateTemplate.ExecuteFind<Supplier>( delegate(ISession session) { return session.CreateQuery("from Supplier s were s.Code = ?") .SetParameter(0, code) .List<Supplier>(); });
where code is a variable in the surrounding block, accessbile inside the anonymous delegate implementation.
A callback implementation effectively can be used for any
Hibernate data access. HibernateTemplate
will
ensure that Session
instances are
properly opened and closed, and automatically participate in
transactions. The template instances are thread-safe and reusable, they
can thus be kept as instance variables of the surrounding class. For
simple single step actions like a single Find, Load, SaveOrUpdate, or
Delete call, HibernateTemplate
offers alternative
convenience methods that can replace such one line callback
implementations. Furthermore, Spring provides a convenient
HibernateDaoSupport
base class that provides a
SessionFactory
property for receiving a
SessionFactory
and for use by subclasses.
In combination, this allows for very simple DAO implementations for
typical requirements:
public class HibernateCustomerDao : HibernateDaoSupport, ICustomerDao { public Customer SaveOrUpdate(Customer customer) { HibernateTemplate.SaveOrUpdate(customer); return customer; } }
As alternative to using Spring's
HibernateTemplate
to implement DAOs, data access
code can also be written in a more traditional fashion, without wrapping
the Hibernate access code in a callback, while still respecting and
participating in Spring's generic
DataAccessException
hierarchy. The
HibernateDaoSupport
base class offers methods to
access the current transactional Session
and to convert exceptions in such a scenario; similar methods are also
available as static helpers on the
SessionFactoryUtils
class. Note that such code
will usually pass 'false
' as the value of the
DoGetSession(..)
methods
'allowCreate
' argument, to enforce running within a
transaction (which avoids the need to close the returned
Session
, as its lifecycle is managed by
the transaction). Asking for the
public class HibernateProductDao extends HibernateDaoSupport implements ProductDao { public Customer SaveOrUpdate(Customer customer) { ISession session = DoGetSession(false); session.SaveOrUpdate(customer); return customer; } } }
This code will not translate the Hibernate exception to a generic DataAccessException.
Hibernate 1.2 introduced a feature called "contextual Sessions",
where Hibernate itself manages one current
ISession
per transaction. This is roughly
equivalent to Spring's synchronization of one Hibernate
Session
per transaction. A corresponding
DAO implementation looks like as follows, based on the plain Hibernate
API:
public class ProductDaoImpl implements IProductDao { private SessionFactory sessionFactory; public ISessionFactory SessionFactory { get { return sessionFactory; } set { sessionFactory = value; } } public IList<Product> LoadProductsByCategory(String category) { return SessionFactory.GetCurrentSession() .CreateQuery("from test.Product product where product.category=?") .SetParameter(0, category) .List<Product>(); } } public class HibernateCustomerDao : ICustomerDao { private ISessionFactory sessionFactory; public ISessionFactory SessionFactory { set { sessionFactory = value; } } public Customer SaveOrUpdate(Customer customer) { sessionFactory.GetCurrentSession().SaveOrUpdate(customer); return customer; } }
The above DAO follows the Dependency Injection pattern: it fits
nicely into a Spring IoC container, just like it would if coded against
Spring's HibernateTemplate
. Of course, such a DAO
can also be set up in plain C# (for example, in unit tests): simply
instantiate it and call SessionFactory
property
with the desired factory reference. As a Spring object definition, it
would look as follows:
<objects> <object id="CustomerDao" type="Spring.Northwind.Dao.NHibernate.HibernateCustomerDao, Spring.Northwind.Dao.NHibernate"> <property name="sessionFactory" ref="MySessionFactory"/> </object> </objects>
The SessionFactory configuration to support this programming model can be done is two ways, both via configuration of Spring's LocalSessionFactoryObject. You can enable the use of Spring's implementation of the NHibernate extension interface, ICurrentSessionContext, by setting the property 'ExposeTransactionAwareSessionFactory' to true on LocalSessionFactoryObject. This is just a short-cut for setting the NHibernate property current_session_context_class with the name of the implementation class to use.
The first way is shown below
<object id="sessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate12">
<property name="ExposeTransactionAwareSessionFactory" value="true" />
<!-- other configuration settings omitted -->
</object>
Which is simply a shortcut for the following configuration
<object id="sessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate12">
<!-- other configuration settings omitted -->
<property name="HibernateProperties">
<dictionary>
<!-- other dictionary entries omitted -->
<entry key="hibernate.current_session_context_class"
value="Spring.Data.NHibernate.SpringSessionContext, Spring.Data.NHibernate12"/>
</dictionary>
</property>
</object>
The main advantage of this DAO style is that it depends on Hibernate API only; no import of any Spring class is required. This is of course appealing from a non-invasiveness perspective, and will no doubt feel more natural to Hibernate developers.
However, the DAO throws plain
HibernateException
which means that callers can
only treat exceptions as generally fatal - unless they want to depend on
Hibernate's own exception hierarchy. Catching specific causes such as an
optimistic locking failure is not possible without tieing the caller to
the implementation strategy. This tradeoff might be acceptable to
applications that are strongly Hibernate-based and/or do not need any
special exception treatment.
Fortunately, Spring's
LocalSessionFactoryObject
supports Hibernate's
SessionFactory.GetCurrentSession()
method for
any Spring transaction strategy, returning the current Spring-managed
transactional Session
even with
HibernateTransactionManager
.
In summary: DAOs can be implemented based on the plain Hibernate 1.2 API, while still being able to participate in Spring-managed transactions. In tihs approach there is one session associated with the transaction.
Transactions can be demarcated in a higher level of the
application, on top of such lower-level data access services spanning
any number of operations. There are no restrictions on the
implementation of the surrounding business service here as well, it just
needs a Spring PlatformTransactionManager
. Again,
the latter can come from anywhere, but preferably as object reference
via a TransactionManager
property - just like
the productDAO
should be set via a
setProductDao(..)
method. The following
snippets show a transaction manager and a business service definition in
a Spring application context, and an example for a business method
implementation.
<objects> TO BE DONE </objects>
public class FulfillmentService : IFulfillmentService private TransactionTemplate transactionTemplate; private IProductDao productDao; private ICustomerDao customerDao; private IOrderDao orderDao; private IShippingService shippingService; public void ProcessCustomer(string customerId) { TO BE DONE } }
Alternatively, one can use Spring's declarative transaction support, which essentially enables you to replace explicit transaction demarcation API calls in your C# code with an AOP transaction interceptor configured in a Spring container. You can either externalize the transaction semantics (like propagation behavior and isolation level ) in an external configuration file or use the Transaction attriubte on the service method to set the transaction semantics.
An example showing attribute driven transaction is shown below
<objects> <object id="HibernateTransactionManager" type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate"> <property name="DbProvider" ref="DbProvider"/> <property name="SessionFactory" ref="MySessionFactory"/> </object> <!-- DAO definition not listed, see above for an example. --> <object id="FulfillmentService" type="Spring.Northwind.Service.FulfillmentService, Spring.Northwind.Service"> <property name="CustomerDao" ref="CustomerDao"/> <property name="OrderDao" ref="OrderDao"/> <property name="ShippingService" ref="ShippingService"/> </object> <!-- Import 'standard xml' configuration for attribute driven declartive tx management --> <import resource="DeclarativeServicesAttributeDriven.xml"/> </objects>
Note that with the new transaction namespace, you can replace the
importing of DeclarativeServicesAttributeDriven.xml with the following
single line, <tx:attribute-driven/>
that more clearly
expresses the intent as compared to the contents of
DeclarativeServicesAttributeDriven.xml.
<objects xmlns="http://www.springframework.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.net/schema/tx"
xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/schema/objects/spring-objects.xsd
http://www.springframework.net/schema/tx http://www.springframework.net/schema/tx/spring-tx-1.1.xsd">
<object id="HibernateTransactionManager"
type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate">
<property name="DbProvider" ref="DbProvider"/>
<property name="SessionFactory" ref="MySessionFactory"/>
</object>
<!-- DAO definition not listed, see above for an example. -->
<object id="FulfillmentService" type="Spring.Northwind.Service.FulfillmentService, Spring.Northwind.Service">
<property name="CustomerDao" ref="CustomerDao"/>
<property name="OrderDao" ref="OrderDao"/>
<property name="ShippingService" ref="ShippingService"/>
</object>
<tx:attribute-driven/>
</objects>
The placement of the transaction attribute in the service layer method is shown below.
public class FulfillmentService : IFulfillmentService { // fields and properties for dao object omitted, see above [Transaction(ReadOnly=false)] public void ProcessCustomer(string customerId) { //Find all orders for customer Customer customer = CustomerDao.FindById(customerId); foreach (Order order in customer.Orders) { //Validate Order Validate(order); //Ship with external shipping service ShippingService.ShipOrder(order); //Update shipping date order.ShippedDate = DateTime.Now; //Update shipment date OrderDao.SaveOrUpdate(order); //Other operations...Decrease product quantity... etc } } }
If you prefer to not use attribute to demarcate your transaction boundaries, you can import a configuration file with the following XML instead of using <tx:attribute-driven/>
<object id="TxProxyConfigurationTemplate" abstract="true" type="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data"> <property name="PlatformTransactionManager" ref="HibernateTransactionManager"/> <property name="TransactionAttributes"> <name-values> <!-- Add common methods across your services here --> <add key="Process*" value="PROPAGATION_REQUIRED"/> </name-values> </property> </object>
Refer to the documentation on Spring Transaction management for configuration of other features, such as rollback rules.
Both TransactionTemplate
and
TransactionInterceptor
(not yet seen explicitly
in above configuration, TransactionProxyFactoryObject uses a
TransactionInterceptor, you would have to specify it explicitly if you
were using an ordingary ProxyFactoryObject.) delegate the actual
transaction handling to a
PlatformTransactionManager
instance, which can be
a HibernateTransactionManager
(for a single
Hibernate SessionFactory
, using a
ThreadLocal
Session
under the hood) or a
TxScopeTransactionManager
(delegating to MS-DTC
for distributed transaction) for Hibernate applications. You could even
use a custom PlatformTransactionManager
implementation. So switching from native Hibernate transaction
management to TxScopeTransactionManager, such as when facing distributed
transaction requirements for certain deployments of your application, is
just a matter of configuration. Simply replace the Hibernate transaction
manager with Spring's TxScopeTransactionManager implementation. Both
transaction demarcation and data access code will work without changes,
as they just use the generic transaction management APIs.
For distributed transactions across multiple Hibernate session
factories, simply combine
TxScopeTransactionManager
as a transaction
strategy with multiple LocalSessionFactoryObject
definitions. Each of your DAOs then gets one specific
SessionFactory
reference passed into it's
respective object property.
TO BE DONE
HibernateTransactionManager
can export the
ADO.NET Transaction
used by Hibernate to
plain ADO.NET access code, for a specific
DbProvider
. (matching connection string).
This allows for high-level transaction demarcation with mixed
Hibernate/ADO.NET data access!
The open session in view pattern keeps the hibernate session open during page rendering so lazily loaded hibernate objects can be displayed. You configure its use by adding an additional custom HTTP module declaration as shown below
<system.web> <httpModules> <add name="OpenSessionInView" type="Spring.Data.NHibernate.Support.OpenSessionInViewModule, Spring.Data.NHibernate"/> </httpModules> ... </system.web>
You can configure which SessionFactory the OpenSessionInViewModule will use by setting 'global' application key-value pairs as shown below. (this will change in future releases)
<appSettings> <add key="Spring.Data.NHibernate.Support.OpenSessionInViewModule.SessionFactoryObjectName" value="SessionFactory"/> </appSettings>