Files
spring-net/doc/reference/src/orm.xml

1077 lines
50 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<!--
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-->
<chapter version="5" xml:id="orm" xmlns="http://docbook.org/ns/docbook"
xmlns:ns6="http://www.w3.org/1999/xlink"
xmlns:ns5="http://www.w3.org/1998/Math/MathML"
xmlns:ns4="http://www.w3.org/1999/xhtml"
xmlns:ns3="http://www.w3.org/2000/svg"
xmlns:ns="http://docbook.org/ns/docbook">
<title>Object Relational Mapping (ORM) data access</title>
<section xml:id="orm-introduction">
<title>Introduction</title>
<para>The Spring Framework provides integration with <emphasis>NHibernate
</emphasis> 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.</para>
<para>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.</para>
<para>Some of the benefits of using the Spring Framework to create your
ORM DAOs include:</para>
<itemizedlist>
<listitem>
<para><emphasis>Ease of testing.</emphasis> Spring's IoC approach
makes it easy to swap the implementations and config locations of
Hibernate <literal>SessionFactory</literal> instances, ADO.NET
<literal>DbProvider</literal> 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.</para>
</listitem>
<listitem>
<para><emphasis>Common data access exceptions.</emphasis> 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.</para>
</listitem>
<listitem>
<para><emphasis>General resource management.</emphasis> Spring
application contexts can handle the location and configuration of
Hibernate <literal>ISessionFactory</literal> instances, ADO.NET
<literal>DbProvider</literal> 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 <literal>Session</literal> for efficiency and proper
transaction handling. Spring makes it easy to transparently create and
bind a <literal>Session</literal> to the current thread, either by
using an explicit 'template' wrapper class at the code level or by
exposing a current <literal>Session</literal> through the Hibernate
<literal>SessionFactory</literal> (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).</para>
</listitem>
<listitem>
<para><emphasis>Integrated transaction management.</emphasis> 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.</para>
</listitem>
</itemizedlist>
<para>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.</para>
<para>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 <link
linkend="orm-hibernate-straight">Implementing DAOs based on the plain
NHibernate API</link>. The NHibernate 1.0 support is in the assembly
Spring.Data.NHibernate and the 1.2 support is in the assembly
Spring.Data.NHibernate12</para>
<para>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.</para>
</section>
<section xml:id="orm-hibernate">
<title>NHibernate</title>
<para>We will start with a coverage of <ulink
url="http://www.hibernate.org/">NHibernate</ulink> 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.</para>
<para>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.</para>
<section xml:id="orm-resource-mngmnt">
<title>Resource management</title>
<para>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 <literal>AdoTemplate</literal> 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
<literal>DataAccessException</literal> 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.</para>
<para>Spring also offers Hibernate support, consisting of a
<literal>HibernateTemplate</literal> analogous to
<literal>AdoTemplate</literal>, a
<literal>HibernateInterceptor</literal>, 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 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 application, 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.</para>
</section>
<section xml:id="orm-tx-mgmt">
<title>Transaction Management</title>
<para>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.</para>
<para>The first strategy is encapsulated in the class
<literal>Spring.Data.NHibernate.HibernateTransactionManager </literal>in
both the <literal>Spring.Data.NHibernate </literal>namespace. This
strategy is preferred when you are using a single database. ADO.NET
operations can also participate in the same transaction, either by using
AdoTemplate or by retrieving the ADO.NET connection/transaction object
pair stored in thread local storage when the transaction begins. Refer
to the documentation of Spring's ADO.NET framework for more information
on retrieving and using the connection/transaction pair without using
AdoTemplate. You can use the HibernateTransactionManager and associated
classes such as SessionFactory, HibernateTemplate directly as you would
any third party API, however they are most commonly used through
Spring's XML configuration file to gain the benefits of easy
configuration for a particular runtime environment and as the basis for
the configuration of a data access layer also configured using XML. An
XML fragment showing the declaration of
<literal>HibernateTransactionManager</literal> is shown below.</para>
<programlisting language="myxml"> &lt;object id="transactionManager"
type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate"&gt;
&lt;property name="DbProvider" ref="DbProvider"/&gt;
&lt;property name="SessionFactory" ref="MySessionFactory"/&gt;
&lt;/object&gt;</programlisting>
<para>The important property of
<literal>HibernateTransactionManager</literal> are the references to the
DbProvider and the Hibernate ISessionFactory. For more information on
the DbProvider, refer to the chapter <link
linkend="dbprovider">DbProvider</link> and the following section on
SessionFactory set up.</para>
<para>The second strategy is to use the class
<literal>Sping.Data.TxScopeTransactionManager</literal> that uses .NET
2.0 System.Transaction namespace and its corresponding TransactionScope
API. This is preferred when you are using multiple transactional
resources, such as multiple databases.</para>
<para>Both 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 <literal>OpenSessionInViewModule</literal> in a web
application in single session mode (see <xref
linkend="orm-hibernate-web" />). In this case the session will be
created on the start of the web request and closed on the end of the
request. Note that the session's flush mode will be set to
<literal>FlushMode.NEVER</literal> at the start of the request. If a
non-readonly transaction is performed, then during the scope of that
transaction processing the flush mode will be changed to AUTO, and then
set back to NEVER at the end of the transaction scope so that any
changes to objects associated with the session during rendering will not
be persisted back to the database when the session is closed at the end
of the web request.</para>
</section>
<section xml:id="orm-session-factory-setup">
<title><literal>SessionFactory</literal> set up in a Spring
container</title>
<para>To avoid tying application objects to hard-coded resource lookups,
Spring allows you to define resources like a
<literal>DbProvider</literal> or a Hibernate
<literal>SessionFactory</literal> 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 <literal>SessionFactory</literal> on
top of it:</para>
<programlisting language="myxml">&lt;objects xmlns="http://www.springframework.net"
xmlns:db="http://www.springframework.net/database"&gt;
&lt;!-- Property placeholder configurer for database settings --&gt;
&lt;object type="Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer, Spring.Core"&gt;
&lt;property name="ConfigSections" value="databaseSettings"/&gt;
&lt;/object&gt;
&lt;!-- Database and NHibernate Configuration --&gt;
&lt;db:provider id="DbProvider"
provider="SqlServer-1.1"
connectionString="Integrated Security=false; Data Source=(local);Integrated Security=true;Database=Northwin;User ID=springqa;Password=springqa;"/&gt;
&lt;object id="MySessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate"&gt;
&lt;property name="DbProvider" ref="DbProvider"/&gt;
&lt;property name="MappingAssemblies"&gt;
&lt;list&gt;
&lt;value&gt;Spring.Northwind.Dao.NHibernate&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;property name="HibernateProperties"&gt;
&lt;dictionary&gt;
&lt;entry key="hibernate.connection.provider"
value="NHibernate.Connection.DriverConnectionProvider"/&gt;
&lt;entry key="hibernate.dialect"
value="NHibernate.Dialect.MsSql2000Dialect"/&gt;
&lt;entry key="hibernate.connection.driver_class"
value="NHibernate.Driver.SqlClientDriver"/&gt;
&lt;/dictionary&gt;
&lt;/property&gt;
&lt;/object&gt;
&lt;/objects&gt;</programlisting>
<para>Many of the properties on
<literal>LocalSessionFactoryObject</literal> are those you will commonly
configure, for example the property <literal>MappingAssemblies</literal>
specifies a list of assemblies to seach for hibernate mapping files. The
property <literal>HibernateProperies</literal> are the familiar
NHibernate properties used to set typical options such as dialect and
driver class. The location of NHibernate mapping information can also be
specified using Spring's <link linkend="resources">IResource
abstraction</link> via the property <literal>MappingResources</literal>.
The IResource abstraction supports opening an input stream from
assemblies, file system, and http(s) based on a Uri syntax. You can also
leverage the extensibility of IResource and thereby allow NHibernate to
obtain its configuration information from locations such as a database
or LDAP.For other properties you can configure them as you normal using
the file <literal>hibernate.cfg.xml</literal> and refer to it via the
property <literal>ConfigFileNames</literal>. This property is a string
array so multiple configuration files are supported.</para>
<para>There are other properties in
<literal>LocalSessionFactoryObject</literal> that relate to the
integration of Spring with NHibernate. The property
<literal>ExposeTransactionAwareSessionFactory</literal> is discussed
below and allows you to use Spring's declarative transaction demarcation
functionality with the standard NHibernate API (as compared to using
HibernateTemplate).</para>
<para>The property <literal>DbProvider</literal> is used to infer two
NHibernate configurations options.</para>
<itemizedlist>
<listitem>
<para>Infer the connection string, typically done via the hibernate
property "hibernate.connection.connection_string".</para>
</listitem>
<listitem>
<para>Delegate to the <literal>DbProvider</literal> itself as the
NHibernate connection provider instead of listing it via property
hibernate.connection.provider via
<literal>HibernateProperties</literal>.</para>
</listitem>
</itemizedlist>
<para>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 <literal>DbProvider</literal> as the
NHibernate connection provider then you can take advantage of
<literal>IDbProvider</literal> implementations that will let you change
the connection string at runtime such as <link lang=""
linkend="dbprovider-usercredentials">UserCredentialsDbProvider</link>
and <link
linkend="dbprovider-multidelegating">MultiDelegatingDbProvider</link>.</para>
<note>
<para><link lang=""
linkend="dbprovider-usercredentials">UserCredentialsDbProvider</link>
and <link
linkend="dbprovider-multidelegating">MultiDelegatingDbProvider</link>
only change the connection string at runtime based on values in thread
local storage and do not clear out the Hibernate cache that is unique
to each <literal>ISessionFactory</literal> instance. As such, they are
only useful for selecting at runtime a single database instance.
Cleaning up an existing session factory when switching to a new
database is left to user code. Creating a new session factory per
connection string (assuming the same mapping files can be used across
all databases connections) is not currently supported. To support this
functionality, you can subclass
<literal>LocalSessionFactoryObject</literal> and override the method
<literal>ISessionFactory NewSessionFactory(Configuration
config)</literal> so that it returns an implementation of
<literal>ISessionFactory</literal> that selects among multiple
instances based on values in thread local storage, much like the
implementation of <literal>MultiDelegatingDbProvider</literal>.</para>
</note>
</section>
<section xml:id="orm-hibernate-template">
<title>The <literal>HibernateTemplate</literal></title>
<para>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
<literal>SessionFactory</literal>. It can get the latter from anywhere,
but preferably as an object reference from a Spring IoC container - via
a simple <methodname>SessionFactory</methodname> property setter. The
following snippets show a DAO definition in a Spring container,
referencing the above defined <literal>SessionFactory</literal>, and an
example for a DAO method implementation.</para>
<programlisting language="myxml">&lt;objects&gt;
&lt;object id="CustomerDao" type="Spring.Northwind.Dao.NHibernate.HibernateCustomerDao, Spring.Northwind.Dao.NHibernate"&gt;
&lt;property name="SessionFactory" ref="MySessionFactory"/&gt;
&lt;/object&gt;
&lt;/objects&gt;</programlisting>
<para></para>
<programlisting language="csharp">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;
}
}</programlisting>
<para>The <literal>HibernateTemplate</literal> class provides many
methods that mirror the methods exposed on the Hibernate
<literal>Session</literal> interface, in addition to a number of
convenience methods such as the one shown above. If you need access to
the <literal>Session</literal> to invoke methods that are not exposed on
the <literal>HibernateTemplate</literal>, you can always drop down to a
callback-based approach like so.</para>
<programlisting language="csharp">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;
}
}</programlisting>
<para>Using the anonymous delegate is particularly convenient when you
would otherwise be passing various method parameter calls to the
interface based version of this callback. Furthermore, when using
generics, you can avoid the typecast and write code like the
following</para>
<programlisting language="csharp">IList&lt;Supplier&gt; suppliers = HibernateTemplate.ExecuteFind&lt;Supplier&gt;(
delegate(ISession session)
{
return session.CreateQuery("from Supplier s were s.Code = ?")
.SetParameter(0, code)
.List&lt;Supplier&gt;();
});</programlisting>
<para>where code is a variable in the surrounding block, accessible
inside the anonymous delegate implementation.</para>
<para>A callback implementation effectively can be used for any
Hibernate data access. <literal>HibernateTemplate</literal> will ensure
that <literal>Session</literal> 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,
<literal>HibernateTemplate</literal> offers alternative convenience
methods that can replace such one line callback implementations.
Furthermore, Spring provides a convenient
<literal>HibernateDaoSupport</literal> base class that provides a
<methodname>SessionFactory</methodname> property for receiving a
<literal>SessionFactory</literal> and for use by subclasses. In
combination, this allows for very simple DAO implementations for typical
requirements:</para>
<programlisting language="csharp">public class HibernateCustomerDao : HibernateDaoSupport, ICustomerDao
{
public Customer SaveOrUpdate(Customer customer)
{
HibernateTemplate.SaveOrUpdate(customer);
return customer;
}
}</programlisting>
</section>
<section xml:id="orm-hibernate-daos">
<title>Implementing Spring-based DAOs without callbacks</title>
<para>As an alternative to using Spring's
<literal>HibernateTemplate</literal> 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 <literal>DataAccessException</literal>
hierarchy. The <literal>HibernateDaoSupport</literal> base class offers
methods to access the current transactional <literal>Session</literal>
and to convert exceptions in such a scenario; similar methods are also
available as static helpers on the
<literal>SessionFactoryUtils</literal> class. Note that such code will
usually pass '<literal>false</literal>' as the value of the
<methodname>DoGetSession(..)</methodname> method's
'<literal>allowCreate</literal>' argument, to enforce running within a
transaction (which avoids the need to close the returned
<literal>Session</literal>, as its lifecycle is managed by the
transaction). Asking for the</para>
<programlisting language="csharp">public class HibernateProductDao : HibernateDaoSupport, IProductDao {
public Customer SaveOrUpdate(Customer customer)
{
ISession session = DoGetSession(false);
session.SaveOrUpdate(customer);
return customer;
}
}
}</programlisting>
<para>This code will <emphasis>not</emphasis> translate the Hibernate
exception to a generic <literal>DataAccessException</literal>.</para>
</section>
<section xml:id="orm-hibernate-straight">
<title>Implementing DAOs based on plain Hibernate 1.2/2.0 API</title>
<para>Hibernate 1.2 introduced a feature called "contextual Sessions",
where Hibernate itself manages one current <literal>ISession</literal>
per transaction. This is roughly equivalent to Spring's synchronization
of one Hibernate <literal>Session</literal> per transaction. A
corresponding DAO implementation looks like as follows, based on the
plain Hibernate API:</para>
<programlisting language="csharp">public class ProductDaoImpl implements IProductDao {
private SessionFactory sessionFactory;
public ISessionFactory SessionFactory
{
get { return sessionFactory; }
set { sessionFactory = value; }
}
public IList&lt;Product&gt; LoadProductsByCategory(String category) {
return SessionFactory.GetCurrentSession()
.CreateQuery("from test.Product product where product.category=?")
.SetParameter(0, category)
.List&lt;Product&gt;();
}
}
public class HibernateCustomerDao : ICustomerDao {
private ISessionFactory sessionFactory;
public ISessionFactory SessionFactory
{
set { sessionFactory = value; }
}
public Customer SaveOrUpdate(Customer customer)
{
sessionFactory.GetCurrentSession().SaveOrUpdate(customer);
return customer;
}
}</programlisting>
<para>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 <literal>HibernateTemplate</literal>. Of course, such a DAO can
also be set up in plain C# (for example, in unit tests): simply
instantiate it and call <methodname>SessionFactory</methodname> property
with the desired factory reference. As a Spring object definition, it
would look as follows:</para>
<programlisting language="myxml">
&lt;objects&gt;
&lt;object id="CustomerDao" type="Spring.Northwind.Dao.NHibernate.HibernateCustomerDao, Spring.Northwind.Dao.NHibernate"&gt;
&lt;property name="sessionFactory" ref="MySessionFactory"/&gt;
&lt;/object&gt;
&lt;/objects&gt;</programlisting>
<para>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.</para>
<para>The first way is shown below</para>
<programlisting language="myxml">&lt;object id="sessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate12"&gt;
&lt;property name="ExposeTransactionAwareSessionFactory" value="true" /&gt;
&lt;!-- other configuration settings omitted --&gt;
&lt;/object&gt;</programlisting>
<para>Which is simply a shortcut for the following configuration</para>
<programlisting language="myxml">&lt;object id="sessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate12"&gt;
&lt;!-- other configuration settings omitted --&gt;
&lt;property name="HibernateProperties"&gt;
&lt;dictionary&gt;
&lt;!-- other dictionary entries omitted --&gt;
&lt;entry key="hibernate.current_session_context_class"
value="Spring.Data.NHibernate.SpringSessionContext, Spring.Data.NHibernate12"/&gt;
&lt;/dictionary&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
<para>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.</para>
<section xml:id="orm-hibernate-straight-ex">
<title>Exception Translation</title>
<para>However, the DAO implemenation as shown throws plain
<literal>HibernateException</literal> 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 tying the
caller to the implementation strategy. This trade off might be
acceptable to applications that are strongly Hibernate-based and/or do
not need any special exception treatment. As an alternative you can
use Spring's exception translation advice to convert the NHibernate
exception to Spring's DataAccessException hierarchy.</para>
<para>Spring offers a solution allowing exception translation to be
applied transparently through the <literal>[Repository]</literal>
attribute:</para>
<programlisting language="csharp">[Repository]
public class HibernateCustomerDao : ICustomerDao {
// class body here
}</programlisting>
<para>and register an exception translation post processor.</para>
<programlisting>&lt;objects&gt;
&lt;!-- configure session factory (omittied for brevity) --&gt;
&lt;!-- Exception translation object post processor --&gt;
&lt;object type="Spring.Dao.Attributes.PersistenceExceptionTranslationPostProcessor, Spring.Data"/&gt;
&lt;!-- Same DAO configuration as before --&gt;
&lt;object id="CustomerDao" type="Spring.Northwind.Dao.NHibernate.HibernateCustomerDao, Spring.Northwind.Dao.NHibernate"&gt;
&lt;property name="sessionFactory" ref="MySessionFactory"/&gt;
&lt;/object&gt;
&lt;/objects&gt;</programlisting>
<para>The postprocessor will automatically look for all exception
translators (implementations of the
<literal>IPersistenceExceptionTranslator</literal> interface) and
advise all object marked with the <literal>[Repository]</literal>
attribute so that the discovered translators can intercept and apply
the appropriate translation on the thrown exceptions. Spring's
<literal>LocalSessionFactory</literal> object implements the
<literal>IPersistenceExceptionTranslator</literal> interface and
performs the same exception translation as was done when using
<literal>HibernateTemplate</literal>.</para>
<para>The <literal>[Repository</literal>] attribute is definedin the
Spring.Data assembly, however it is used as a 'marker' attribute, and
you can provide your own if you would like to avoid coupling your DAO
implementation to a Spring attribute. This is done by setting
<literal>PersistenceExceptionTranslationPostProcessor's</literal>
property <literal>RepositoryAttributeType</literal> to your own
attribute type.</para>
<para><note>
<para>In summary: DAOs can be implemented based on the plain
Hibernate 1.2/2.0 API, while still being able to participate in
Spring-managed transactions and exception translation.</para>
</note></para>
</section>
</section>
<section xml:id="orm-hibernate-tx-programmatic">
<title>Programmatic transaction demarcation</title>
<para>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 <literal>PlatformTransactionManager</literal>. Again, the
latter can come from anywhere, but preferably as an object reference via
a <methodname>TransactionManager</methodname> property - just like the
<literal>productDAO</literal> should be set via a
<methodname>setProductDao(..)</methodname> 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.</para>
<programlisting language="myxml">&lt;objects&gt;
&lt;object id="TransactionManager"
type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate"&gt;
&lt;property name="DbProvider" ref="DbProvider"/&gt;
&lt;property name="SessionFactory" ref="MySessionFactory"/&gt;
&lt;/object&gt;
&lt;!-- DAO definition not listed, see above for an example. --&gt;
&lt;object id="FulfillmentService" type="Spring.Northwind.Service.FulfillmentService, Spring.Northwind.Service"&gt;
&lt;property name="CustomerDao" ref="CustomerDao"/&gt;
&lt;property name="OrderDao" ref="OrderDao"/&gt;
&lt;property name="ShippingService" ref="ShippingService"/&gt;
&lt;property name="TransactionManager" ref="TransactionManager"/&gt;
&lt;/object&gt;
&lt;/objects&gt;</programlisting>
<programlisting language="csharp">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;
});
}
}</programlisting>
</section>
<section xml:id="orm-hibernate-tx-declarative">
<title>Declarative transaction demarcation</title>
<para>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.</para>
<para>An example showing attribute driven transaction is shown
below</para>
<programlisting language="myxml">&lt;objects&gt;
&lt;object id="TransactionManager"
type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate"&gt;
&lt;property name="DbProvider" ref="DbProvider"/&gt;
&lt;property name="SessionFactory" ref="MySessionFactory"/&gt;
&lt;/object&gt;
&lt;!-- DAO definition not listed, see above for an example. --&gt;
&lt;object id="FulfillmentService" type="Spring.Northwind.Service.FulfillmentService, Spring.Northwind.Service"&gt;
&lt;property name="CustomerDao" ref="CustomerDao"/&gt;
&lt;property name="OrderDao" ref="OrderDao"/&gt;
&lt;property name="ShippingService" ref="ShippingService"/&gt;
&lt;/object&gt;
&lt;!-- Import 'standard xml' configuration for attribute driven declarative tx management --&gt;
&lt;import resource="DeclarativeServicesAttributeDriven.xml"/&gt;
&lt;/objects&gt;</programlisting>
<para>Note that with the new transaction namespace, you can replace the
importing of DeclarativeServicesAttributeDriven.xml with the following
single line, <code>&lt;tx:attribute-driven/&gt;</code> that more clearly
expresses the intent as compared to the contents of
DeclarativeServicesAttributeDriven.xml.</para>
<programlisting language="myxml">&lt;objects xmlns="http://www.springframework.net"
xmlns:tx="http://www.springframework.net/schema/tx"&gt;
&lt;object id="transactionManager"
type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate"&gt;
&lt;property name="DbProvider" ref="DbProvider"/&gt;
&lt;property name="SessionFactory" ref="MySessionFactory"/&gt;
&lt;/object&gt;
&lt;!-- DAO definition not listed, see above for an example. --&gt;
&lt;object id="FulfillmentService" type="Spring.Northwind.Service.FulfillmentService, Spring.Northwind.Service"&gt;
&lt;property name="CustomerDao" ref="CustomerDao"/&gt;
&lt;property name="OrderDao" ref="OrderDao"/&gt;
&lt;property name="ShippingService" ref="ShippingService"/&gt;
&lt;/object&gt;
<emphasis>&lt;tx:attribute-driven/&gt;</emphasis>
&lt;/objects&gt;</programlisting>
<para>The placement of the transaction attribute in the service layer
method is shown below.</para>
<programlisting language="csharp">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
}
}
}</programlisting>
<para>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 &lt;tx:attribute-driven/&gt;</para>
<programlisting language="myxml"> &lt;object id="TxProxyConfigurationTemplate" abstract="true"
type="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data"&gt;
&lt;property name="PlatformTransactionManager" ref="HibernateTransactionManager"/&gt;
&lt;property name="TransactionAttributes"&gt;
&lt;name-values&gt;
&lt;!-- Add common methods across your services here --&gt;
&lt;add key="Process*" value="PROPAGATION_REQUIRED"/&gt;
&lt;/name-values&gt;
&lt;/property&gt;
&lt;/object&gt;</programlisting>
<para>Refer to the documentation on Spring Transaction management for
configuration of other features, such as rollback rules.</para>
</section>
<section xml:id="orm-hibernate-tx-strategies">
<title>Transaction management strategies</title>
<para>Both <literal>TransactionTemplate</literal> and
<literal>TransactionInterceptor</literal> (not yet seen explicitly in
above configuration, TransactionProxyFactoryObject uses a
TransactionInterceptor, you would have to specify it explicitly if you
were using an ordinary ProxyFactoryObject.) delegate the actual
transaction handling to a <literal>PlatformTransactionManager</literal>
instance, which can be a <literal>HibernateTransactionManager</literal>
(for a single Hibernate <literal>SessionFactory</literal>, using a
<literal>ThreadLocal</literal> <literal>Session</literal> under the
hood) or a <literal>TxScopeTransactionManager</literal> (delegating to
MS-DTC for distributed transaction) for Hibernate applications. You
could even use a custom <literal>PlatformTransactionManager</literal>
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.</para>
<para>For distributed transactions across multiple Hibernate session
factories, simply combine <literal>TxScopeTransactionManager</literal>
as a transaction strategy with multiple
<literal>LocalSessionFactoryObject</literal> definitions. Each of your
DAOs then gets one specific <literal>SessionFactory</literal> reference
passed into it's respective object property.</para>
<programlisting>TO BE DONE
</programlisting>
<para><literal>HibernateTransactionManager</literal> can export the
ADO.NET <literal>Transaction</literal> used by Hibernate to plain
ADO.NET access code, for a specific <literal>DbProvider</literal>.
(matching connection string). This allows for high-level transaction
demarcation with mixed Hibernate/ADO.NET data access!</para>
</section>
<section xml:id="orm-hibernate-web">
<title>Web Session Management</title>
<para>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</para>
<programlisting language="myxml"> &lt;system.web&gt;
&lt;httpModules&gt;
&lt;add name="OpenSessionInView" type="Spring.Data.NHibernate.Support.OpenSessionInViewModule, Spring.Data.NHibernate"/&gt;
&lt;/httpModules&gt;
...
&lt;/system.web&gt;</programlisting>
<para>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)</para>
<programlisting language="myxml"> &lt;appSettings&gt;
&lt;add key="Spring.Data.NHibernate.Support.OpenSessionInViewModule.SessionFactoryObjectName" value="SessionFactory"/&gt;
&lt;/appSettings&gt;</programlisting>
<para>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
<literal>Spring.Data.NHibernate.Support.OpenSessionInViewModule.EntityInterceptorObjectName</literal>
and if SingleSession mode is used via the key
<literal>Spring.Data.NHibernate.Support.OpenSessionInViewModule.SingleSession</literal>.
If SingleSession is set to false, referred to as 'deferred close mode',
then each transaction scope will use a new Session and kept open until
the end of the web request. This has the drawback that the first level
cache is not reused across transactions and that objects are required to
be unique across all sessions. Problems can arise if the same object is
associated with more than one hibernate session.</para>
<important>
<para>By default, OSIV applies <literal>FlushMode.NEVER</literal> on
every session it creates. This is because if OSIV flushed pending
changes during "EndRequest" and an error occurs, all response has
already been sent to the client. There would be no way of telling the
client about the error.</para>
<para>By default this means you MUST explicitly demarcate transaction
boundaries around non-readonly statements when using OSIV. For
configuring transactions see <xref
linkend="orm-hibernate-tx-declarative" /> or the
<command>Spring.Data.NHibernate.Northwind</command> example
application.</para>
</important>
</section>
<section xml:id="orm-hibernate-sessionscope">
<title>Session Scope</title>
<para>The class Spring.Data.NHibernate.Support.SessionScope allows for
you to use a single NHibernate session across multiple transactions. The
usage is shown below</para>
<programlisting language="csharp">using (new SessionScope())
{
... do multiple operations with a single session, possibly in multiple transactions.
}</programlisting>
<para>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.</para>
</section>
<section xml:id="orm-hibernate-integration-testing">
<title>Integration Testing</title>
<para>When using <link linkend="testing">Spring's Integration Testing
support</link>, 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</para>
<programlisting>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;
}
}</programlisting>
<para></para>
</section>
</section>
</chapter>