![]() |
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
<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 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. public class HibernateCustomerDao : HibernateDaoSupport, ICustomerDao { public Customer SaveOrUpdate(Customer customer) { HibernateTemplate.SaveOrUpdate(customer); return customer; } } As an alternative to using Spring's
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 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
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:
Note that the only mandatory portion of the string is the propagation setting. The default transactions semantics which apply are as follows:
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
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. 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
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.
|