![]() |
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 to 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:
The NHibernate Northwind example in the Spring distribution shows a NHibernate 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 serves as a working sample application that illustrates the use of NHibernate in a Spring web application. It also leverages declarative transaction demarcation with different transaction strategies. Both NHibernate 1.0 and NHibernate 1.2 are supported. Differences relate to the use of generics and new features such as contextual sessions. For information on the latter, refer to the section Implementing DAOs based on the plain NHibernate API. The NHibernate 1.0 support is in the assembly Spring.Data.NHibernate and the 1.2 support is in the assembly Spring.Data.NHibernate12 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 participate 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 separate .dlls with the same 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 a common
infrastructure exception hierarchy. Spring introduces a DAO exception
hierarchy, applicable to any data access strategy. For direct ADO.NET,
the Spring also offers Hibernate support, consisting of a
While NHibernate offers an API for transaction management you will quite likely find the benefits of using Spring's generic transaction management features to be more compelling to use, typically for use of a declarative programming model for transaction demarcation and easily mixing ADO.NET and NHibernate operations within a single transaction. See the chapter on transaction management for more information on Spring's transaction management features. There are two choices for transaction management strategies, one based on the NHibernate API and the other the .NET 2.0 TransactionScope API. The first strategy is encapsulated in the class
<object id="transactionManager" type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate"> <property name="DbProvider" ref="DbProvider"/> <property name="SessionFactory" ref="MySessionFactory"/> </object> The important property of
The second strategy is to use the class
All of these strategies associate one Hibernate Session for the
scope of the transaction (scope in the general demarcation sense, not
System.Transaction sense). If there is no transaction then a new Session
will be opened for each operation. The exception to this rule is when
using the To avoid tying application objects to hard-coded resource lookups,
Spring allows you to define resources like a
<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:provider id= Many of the properties on
There are other properties in
The property
If you specify both the property hibernate.connection.provider and
DbProvider (as shown above) the configuration of the property
hibernate.connection.provider is used and a warning level message is
logged. If you use Spring's
Beginning with Spring.NET 1.3.1 there is direct support for
creating a new session factory per connection string (assuming the
same mapping files can be used across all databases connections). To
support this functionality,
Direct support for configuration of NHibernate mapping files using FluentNHibernate will be included in a future release. Until then, to see how you can extend LocalSessionFactoryObject to suppport using FluentNHibernate follow the instructions on Benny Michielson's blog post here. Introduced in Hibernate 2.1 is support for dependency
injection of hibernate managed objects via the
IBytecodeProvider extension point. As of Spring
1.3 provides
Spring.Data.NHibernate.Bytecode.BytecodeProvider
as the default IBytecodeProvider implementation
when using LocalSessionFactory object to
configure an ISessionFactory. To use a
different IBytecodeProvider configure it via
the standard the Hibernate means, using App.confg or Web.config via
the element Hibernate 1.2 introduced a feature called "contextual Sessions",
where Hibernate itself manages one current 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 <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 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 the 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 implemenation as shown throws plain
Spring offers a solution allowing exception translation to be
applied transparently through the [Repository] public class HibernateCustomerDao : ICustomerDao { // class body here } and register an exception translation post processor. <objects> <!-- configure session factory (omittied for brevity) --> <!-- Exception translation object post processor --> <object type="Spring.Dao.Attributes.PersistenceExceptionTranslationPostProcessor, Spring.Data"/> <!-- Same DAO configuration as before --> <object id="CustomerDao" type="Spring.Northwind.Dao.NHibernate.HibernateCustomerDao, Spring.Northwind.Dao.NHibernate"> <property name="sessionFactory" ref="MySessionFactory"/> </object> </objects> The postprocessor will automatically look for all exception
translators (implementations of the
The
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 a configuration file or use the Transaction attribute on the service method to set the transaction semantics. An example showing attribute driven transaction is shown below <objects> <object id="TransactionManager" 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 declarative 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, <objects xmlns="http://www.springframework.net" xmlns:tx="http://www.springframework.net/schema/tx"> <object id="transactionManager" 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. 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 <objects> <object id="TransactionManager" 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"/> <property name="TransactionManager" ref="TransactionManager"/> </object> </objects> public class FulfillmentService : IFulfillmentService private TransactionTemplate transactionTemplate; private IProductDao productDao; private ICustomerDao customerDao; private IOrderDao orderDao; private IShippingService shippingService; public TransactionManager TransactionManager { set { transactionTemplate = new TransactionTemplate(value); } public void ProcessCustomer(string customerId) { tt.Execute(delegate(ITransactionStatus status) { //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 } return null; }); } } Both For distributed transactions across multiple Hibernate session
factories, simply combine TO BE DONE
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> The default behavior of the module is that a single session is
currently used for the life of the request. Refer to the earlier section
on Transaction Management in this chapter for more information on how
sessions are managed in the OpenSessionInViewModule. You can also
configure in the application setting the EntityInterceptorObjectName
using the key
The class Spring.Data.NHibernate.Support.SessionScope allows for you to use a single NHibernate session across multiple transactions. The usage is shown below using (new SessionScope()) { ... do multiple operations with a single session, possibly in multiple transactions. } Refer to the API documentation for information on overloaded constructor. At the end of the using block the session is automatically closed. All transactions within the scope use the same session, if you are using Spring's HibernateTemplate or using Spring's implementation of NHibernate 1.2's ICurrentSessionContext interface. See other sections in this chapter for further information on those usage scenarios. When using Spring's Integration Testing support, you should make sure that the hibernate session is flushed so that the database is updated, as compared to just updating the hibernate session cache. You can implement a base class as shown below to help with the integration testing public abstract class NHibernateIntegrationTests : AbstractTransactionalSpringContextTests { private SessionFactory sessionFactory; public ISessionFactory SessionFactory { get { return sessionFactory; } set { sessionFactory = value; } } protected override void OnSetUpInTransaction() { base.OnSetUpInTransaction(); Assert.IsNotNull(SessionFactory); SessionFactory.GetCurrentSession().FlushMode = FlushMode.Always; SessionFactory.GetCurrentSession().CacheMode = CacheMode.Ignore; } }
|