Appendix A. Classic Spring Usage

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.

A.1. Classic Hibernate Usage

For the currently recommended usage patterns for NHibernate see Section 21.2, “NHibernate”

A.1.1. The HibernateTemplate

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;
    }
}

A.1.2. Implementing Spring-based DAOs without callbacks

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.

A.2. Classic Declarative Transaction Configurations

A.2.1. Declarative Transaction Configuration using DefaultAdvisorAutoProxyCreator

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 metadata” 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.

A.2.2. Declarative Transactions using TransactionProxyFactoryObject

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 enumberateion, 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.

A.2.3. Concise proxy definitions

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>

A.2.4. Declarative Transactions using ProxyFactoryObject

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.