This appendix discusses some classic Spring usage patterns as a reference for developers maintaining legacy Spring applications. These usage patterns no longer reflect the recommended way of using these features and the current recommended usage is covered in the respective sections of the reference manual.
For the currently recommended usage patterns for NHibernate see Section 21.2, “NHibernate”
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 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
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, accessible 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 an 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(..)
method's
'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 : HibernateDaoSupport, IProductDao { 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
.
Using the DefaultAdvisorAutoProxyCreator to configure declarative transactions enables you to refer to the transaction attribute as the pointcut to use for the transactional advice for any object definition defined in the IoC container. The configuration to create a transactional proxy for the manager class shown in the chapter on transaction management is shown below.
<!-- The rest of the config file is common no matter how many objects you add --> <!-- that you would like to have declarative tx management applied to --> <object id="autoProxyCreator" type="Spring.Aop.Framework.AutoProxy.DefaultAdvisorAutoProxyCreator, Spring.Aop"> </object> <object id="transactionAdvisor" type="Spring.Transaction.Interceptor.TransactionAttributeSourceAdvisor, Spring.Data"> <property name="TransactionInterceptor" ref="transactionInterceptor"/> </object> <!-- Transaction Interceptor --> <object id="transactionInterceptor" type="Spring.Transaction.Interceptor.TransactionInterceptor, Spring.Data"> <property name="TransactionManager" ref="transactionManager"/> <property name="TransactionAttributeSource" ref="attributeTransactionAttributeSource"/> </object> <object id="attributeTransactionAttributeSource" type="Spring.Transaction.Interceptor.AttributesTransactionAttributeSource, Spring.Data"> </object>
Granted this is a bit verbose and hard to grok at first sight - however you only need to grok this once as it is 'boiler plate' XML you can reuse across multiple projects. What these object definitions are doing is to instruct Spring's to look for all objects within the IoC configuration that have the [Transaction] attribute and then apply the AOP transaction interceptor to them based on the transaction options contained in the attribute. The attribute serves both as a pointcut and as the declaration of transactional option information.
Since this XML fragment is not tied to any specific object references it can be included in its own file and then imported via the <import> element. In examples and test code this XML configuration fragment is named autoDeclarativeServices.xml See Section 5.2.2.3, “Composing XML-based configuration meta data” for more information.
The classes and their roles in this configuration fragment are listed below
TransactionInterceptor
is the AOP advice
responsible for performing transaction management
functionality.
TransactionAttributeSourceAdvisor
is an AOP
Advisor that holds the TransactionInterceptor, which is the advice,
and a pointcut (where to apply the advice), in the form of a
TransactionAttributeSource.
AttributesTransactionAttributeSource
is an
implementation of the ITransactionAttributeSource
interface that defines where to get the transaction metadata
defining the transaction semantics (isolation level, propagation
behavior, etc) that should be applied to specific methods of
specific classes. The transaction metadata is specified via
implementations of the
ITransactionAttributeSource
interface. This
example shows the use of the implementation
Spring.Transaction.Interceptor.AttributesTransactionAttributeSource
to obtain that information from standard .NET attributes. By the
very nature of using standard .NET attributes, the attribute serves
double duty in identifying the methods where the transaction
semantics apply. Alternative implementations of
ITransactionAttributeSource
available are
MatchAlwaysTransactionAttributeSource
,
NameMatchTransactionAttributeSource
, or
MethodMapTransactionAttributeSource
.
MatchAlwaysTransactionAttributeSource
is configured with a ITransactionAttribute instance that is
applied to all methods. The shorthand string representation,
i.e. PROPAGATION_REQUIRED can be used
AttributesTransactionAttributeSource
:
Use a standard. .NET attributes to specify the transactional
information. See TransactionAttribute
class
for more information.
NameMatchTransactionAttributeSource
allows ITransactionAttributes to be matched by method name. The
NameMap IDictionary property is used to specify the mapping. For
example
<object name="nameMatchTxAttributeSource" type="Spring.Transaction.Interceptor.NameMatchTransactionAttributeSource, Spring.Data" <property name="NameMap"> <dictionary> <entry key="Execute" value="PROPAGATION_REQUIRES_NEW, -ApplicationException"/> <entry key="HandleData" value="PROPAGATION_REQUIRED, -DataHandlerException"/> <entry key="Find*" value="ISOLATION_READUNCOMMITTED, -DataHandlerException"/> </dictionary> </property> </object>
Key values can be prefixed and/or suffixed with wildcards as well as include the full namespace of the containing class.
MethodMapTransactionAttributeSource
:
Similar to NameMatchTransactionAttributeSource but specifies
that only fully qualified method names (i.e. type.method,
assembly) and wildcards can be used at the start or end of the
method name for matching multiple methods.
DefaultAdvisorAutoProxyCreator
: looks for
Advisors in the context, and automatically creates proxy objects
which are the transactional wrappers
Refer to the following section for a more convenient way to achieve the same goal of declarative transaction management using attributes.
The TransactionProxyFactoryObject is easier to use than a ProxyFactoryObject for most cases since the transaction interceptor and transaction attributes are properties of this object. This removes the need to declare them as separate objects. Also, unlike the case with the ProxyFactoryObject, you do not have to give fully qualified method names, just the normal 'short' method name. Wild card matching on the method name is also allowed, which in practice helps to enforce a common naming convention for the methods of your DAOs. The example from chapter 5 is shown here using a TransactionProxyFactoryObject.
<object id="testObjectManager" type="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data"> <property name="PlatformTransactionManager" ref="adoTransactionManager"/> <property name="Target"> <object type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests"> <property name="TestObjectDao" ref="testObjectDao"/> </object> </property> <property name="TransactionAttributes"> <name-values> <add key="Save*" value="PROPAGATION_REQUIRED"/> <add key="Delete*" value="PROPAGATION_REQUIRED"/> </name-values> </property> </object>
Note the use of an inner object definition for the target which will make it impossible to obtain an unproxied reference to the TestObjectManager.
As can be seen in the above definition, the TransactionAttributes property holds a collection of name/value pairs. The key of each pair is a method or methods (a * wildcard ending is optional) to apply transactional semantics to. Note that the method name is not qualified with a package name, but rather is considered relative to the class of the target object being wrapped. The value portion of the name/value pair is the TransactionAttribute itself that needs to be applied. When specifying it as a string value as in this example, it's in String format as defined by TransactionAttributeConverter. This format is:
PROPAGATION_NAME,ISOLATION_NAME,readOnly,timeout_NNNN,+Exception1,-Exception2
Note that the only mandatory portion of the string is the propagation setting. The default transactions semantics which apply are as follows:
Exception Handling: All exceptions thrown trigger a rollback.
Transactions are read/write
Isolation Level: TransactionDefinition.ISOLATION_DEFAULT
Timeout: TransactionDefinition.TIMEOUT_DEFAULT
Multiple rollback rules can be specified here, comma-separated. A
- prefix forces rollback; a + prefix specifies commit. Under the covers
the IDictionary of name value pairs will be converted to an instance of
NameMatchTransactionAttributeSource
The string used for PROPAGATION_NAME are those defined on the Spring.Transaction.TransactionPropagation enumeration, namely Required, Supports, Mandatory, RequiresNew, NotSupported, Never, Nested. The string used for ISOLATION_NAME are those defined on the System.Data.IsolationLevel enumeration, namely ReadCommitted, ReadUncommitted, RepeatableRead, Serializable.
The TransactionProxyFactoryObject allows you to set optional "pre" and "post" advice, for additional interception behavior, using the "PreInterceptors" and "PostInterceptors" properties. Any number of pre and post advices can be set, and their type may be Advisor (in which case they can contain a pointcut), MethodInterceptor or any advice type supported by the current Spring configuration (such as ThrowsAdvice, AfterReturningAdvice or BeforeAdvice, which are supported by default.) These advices must support a shared-instance model. If you need transactional proxying with advanced AOP features such as stateful mixins, it's normally best to use the generic ProxyFactoryObject, rather than the TransactionProxyFactoryObject convenience proxy creator.
Using abstract object definitions in conjunction with a TransactionProxyFactoryObject provides you a more concise means to reuse common configuration information instead of duplicating it over and over again with a definition of a TransactionProxyFactoryObject per object. Objects that are to be proxied typically have the same pattern of method names, Save*, Find*, etc. This commonality can be placed in an abstract object definition, which other object definitions refer to and change only the configuration information that is different. An abstract object definition is shown below
<object id="txProxyTemplate" abstract="true" type="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data"> <property name="PlatformTransactionManager" ref="adoTransactionManager"/> <property name="TransactionAttributes"> <name-values> <add key="Save*" value="PROPAGATION_REQUIRED"/> <add key="Delete*" value="PROPAGATION_REQUIRED"/> </name-values> </property> </object>
Subsequent definitions can refer to this 'base' configuration as shown below
<object id="testObjectManager" parent="txProxyTemplate"> <property name="Target"> <object type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests"> <property name="TestObjectDao" ref="testObjectDao"/> </object> </property> </object>
Using the general ProxyFactoryObject to declare transactions gives you a great deal of control over the proxy created since you can specify additional advice, such as for logging or performance. Based on the example shown previously a sample configuration using ProxyFactoryObject is shown below
<object id="testObjectManagerTarget" type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests"> <property name="TestObjectDao" ref="testObjectDao"/> </object> <object id="testObjectManager" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop"> <property name="Target" ref="testObjectManagerTarget"/> <property name="ProxyInterfaces"> <value>Spring.Data.ITestObjectManager</value> </property> <property name="InterceptorNames"> <value>transactionInterceptor</value> </property> </object>
The ProxyFactoryObject will create a proxy for the Target, i.e. a TestObjectManager instance. An inner object definition could also have been used such that it would make it impossible to obtain an unproxied object from the container. The interceptor name refers to the following definition.
<object id="transactionInterceptor" type="Spring.Transaction.Interceptor.TransactionInterceptor, Spring.Data"> <property name="TransactionManager" ref="adoTransactionManager"/> <!-- note do not have converter from string to this property type registered --> <property name="TransactionAttributeSource" ref="methodMapTransactionAttributeSource"/> </object> <object name="methodMapTransactionAttributeSource" type="Spring.Transaction.Interceptor.MethodMapTransactionAttributeSource, Spring.Data"> <property name="MethodMap"> <dictionary> <entry key="Spring.Data.TestObjectManager.SaveTwoTestObjects, Spring.Data.Integration.Tests" value="PROPAGATION_REQUIRED"/> <entry key="Spring.Data.TestObjectManager.DeleteTwoTestObjects, Spring.Data.Integration.Tests" value="PROPAGATION_REQUIRED"/> </dictionary> </property> </object>
The transaction options for each method are specified using a dictionary containing the class name + method name, assembly as the key and the value is of the form
<Propagation Behavior>, <Isolation Level>, <ReadOnly>, -Exception, +Exception
All but the propagation behavior are optional. The + and - are used in front of the name of an exception. Minus indicates to rollback if the exception is thrown, the Plus indicates to commit if the exception is thrown.