From 4590dfb6d8b9150446d9840551c4d457c3e4cb2e Mon Sep 17 00:00:00 2001 From: markpollack Date: Wed, 16 Dec 2009 17:33:31 +0000 Subject: [PATCH] SPRNET-1299 - Move older ways of configuring transaction management, using HibernateTemplate, to a 'Classic Spring Usage' section --- doc/reference/src/classic-spring.xml | 538 ++++++++++++++++++++++++++ doc/reference/src/index.xml | 13 +- doc/reference/src/orm.xml | 149 -------- doc/reference/src/transaction.xml | 548 +++------------------------ 4 files changed, 607 insertions(+), 641 deletions(-) create mode 100644 doc/reference/src/classic-spring.xml diff --git a/doc/reference/src/classic-spring.xml b/doc/reference/src/classic-spring.xml new file mode 100644 index 00000000..21e5e490 --- /dev/null +++ b/doc/reference/src/classic-spring.xml @@ -0,0 +1,538 @@ + + + + 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. + +
+ Classic Hibernate Usage + + For the currently recommended usage patterns for NHibernate see + + +
+ The <literal>HibernateTemplate</literal> + + 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; + } +} +
+ +
+ 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. +
+
+ +
+ Classic Declarative Transaction Configurations + +
+ 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 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. +
+
+ 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. +
+ +
+ 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> +
+ +
+ 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. +
+
+ +
diff --git a/doc/reference/src/index.xml b/doc/reference/src/index.xml index 58244363..2379e653 100644 --- a/doc/reference/src/index.xml +++ b/doc/reference/src/index.xml @@ -55,6 +55,7 @@ + ]> The Spring.NET Framework Reference Documentation Version 1.3.0 - Last Updated December 15, 2009 (Latest documentation) + Last Updated December 17, 2009 (Latest documentation) Mark @@ -452,8 +453,12 @@ - &xsd-configuration; - &xml-custom; - &xsd; + + Appendices + &classic-spring; + &xsd-configuration; + &xml-custom; + &xsd; + diff --git a/doc/reference/src/orm.xml b/doc/reference/src/orm.xml index c3f3ab82..cbde9422 100644 --- a/doc/reference/src/orm.xml +++ b/doc/reference/src/orm.xml @@ -591,155 +591,6 @@ public class HibernateCustomerDao : ICustomerDao { -
- The <literal>HibernateTemplate</literal> for Hibernate - 1.2 - - 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; - } -} -
- -
- Implementing Spring-based DAOs without HibernateTemplate in - Hibernate 1.2 - - 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. -
Declarative transaction demarcation diff --git a/doc/reference/src/transaction.xml b/doc/reference/src/transaction.xml index 992a3f72..a89d60b0 100644 --- a/doc/reference/src/transaction.xml +++ b/doc/reference/src/transaction.xml @@ -439,16 +439,16 @@ lightweight container. Spring's declarative transaction management is made possible with - Spring AOP, although, as the transactional aspects code comes with Spring - and may be used in a boilerplate fashion, AOP concepts do not generally - have to be understood to make effective use of this code. + Spring's aspect-oriented programming (AOP), although, as the transactional + aspects code comes with Spring and may be used in a boilerplate fashion, + AOP concepts do not generally have to be understood to make effective use + of this code. - The basic approach is to specify transaction behavior (or lack of - it) down to the individual method level. It is also possible to mark a - transaction for rollback by setting the 'RollbackOnly' property on the - ITransactionStatus object returned from the IPlatformTransactionManager - within a transaction context if necessary. Some of the highlights of - Spring's declarative transaction management are: + The approach is to specify transaction behavior (or lack of it) down + to the individual method level. It is also possible to mark a transaction + for rollback by calling the SetRollbackOnly() + method within a transaction context if necessary. Some of the highlights + of Spring's declarative transaction management are: @@ -482,20 +482,17 @@ - Note rollback rules as configured from XML are still under - development. - The concept of rollback rules is important: they enable us to specify which exceptions should cause automatic roll back. We specify this - declaratively, in configuration, not in code. So, while we can still set - RollbackOnly on the + declaratively, in configuration, not in code. So, although you can still + call SetRollbackOnly() on the ITransactionStatus object to roll the current - transaction back Programatically, most often we can specify a rule that + transaction back, most often you can specify a rule that MyApplicationException must always result in rollback. This has the - significant advantage that business objects don't need to depend on the + significant advantage that business objects do not depend on the transaction infrastructure. For example, they typically don't need to - import any Spring APIs, transaction or other. If you would like to - rollback the transaction programmatically and you are using declarative + import any Spring transaction APIs or other Spring APIs. However, to + rollback the transaction programmatically when using declarative transaction management, use the utility method TransactionInterceptor.CurrentTransactionStatus.SetRollbackOnly(); @@ -510,37 +507,26 @@ Understanding Spring's declarative transaction implementation - The aim of this section is to dispel the mystique that is - sometimes associated with the use of declarative transactions. It is all - very well for this reference documentation to simply tell you to - annotate your classes with the Transaction attribute and add some - boilerplate XML to your IoC configuration, and then expect you to - understand how it all works. This section will explain the inner - workings of Spring's declarative transaction infrastructure to help you - navigate your way back upstream to calmer waters in the event of - transaction-related issues. + It is not sufficient to tell you simply to annotate your classes + with the [Transaction] attribute, add the line + (<tx:attribute-driven/>) to your configuration, + and then expect you to understand how it all works. This section + explains the inner workings of the Spring Framework's declarative + transaction infrastructure in the event of transaction-related + issues. - - Looking at the Spring source code is a good way to get a real - understanding of Spring's transaction support. You should find the API - documentation informative and complete. We suggest turning the logging - level to 'DEBUG' in your Spring-enabled application(s) during - development to better see what goes on under the hood. - - - The most important concepts to grasp with regard to Spring's - declarative transaction support are that this support is enabled via AOP - proxies, and that the transactional advice is driven by metadata - (currently XML- or attribute-based). The combination of a proxy with - transactional metadata yields an AOP proxy that uses a - TransactionInterceptor in conjunction with an - appropriate IPlatformTransactionManager + The most important concepts to grasp with regard to the Spring + Framework's declarative transaction support are that this support is + enabled via AOP proxies, and + that the transactional advice is driven by metadata (currently XML- or + attribute-based). The combination of AOP with transactional metadata + yields an AOP proxy that uses a + TransactionInterceptor in conjunction with an + appropriate IPlatformTransactionManager implementation to drive transactions around method invocations. - Although knowledge of AOP (and specifically Spring AOP) is not - required in order to use Spring's declarative transaction support, it - can help. Spring AOP is thoroughly covered in the AOP chapter. + Spring AOP is covered in Conceptually, calling a method on a transactional proxy looks like @@ -551,96 +537,26 @@ - - The flow of events is the following. First the set of objects you - would like to apply AOP transactional advice to are identified. There - are a variety of ways to configure the Spring IoC container to create - proxies for the defined object definitions. The standard Spring AOP - based options are - - - - ProxyFactoryObject. The common properties - to set are the reference to the object to proxy (the target object) - and a reference to the transaction advice. See for more details. - - - - AutoProxy - Defines criteria to select a collection of objects - to create a transactional AOP proxy. - - The AutoProxy options are - - - - ObjectNameAutoProxyCreator which - specifies a collection of object names based on wildcard - matching of object names. See - - - - DefaultAdvisorAutoProxyCreator which - specifies one or more "advisors" i.e an object representing an - aspect, including both an advice and a pointcut targeting it to - specific joinpoints. See - - - - - - There is also a convenience subclass of - ProxyFactoryObject, namely - TransactionProxyFactoryObject, that sets some common - default values for the specific case of applying transactional - advice. - - The DefaultAdvisorAutoProxyCreator is very - powerful and is the means by which Spring can be configured to use - attributes to identify the pointcuts where transaction advice should be - applied. The advisor that performs that task is - TransactionAttributeSourceAdvisor. - Note, think of the word 'Attribute' in this class name not as - the .NET attribute but as the transaction 'options' you want to - specify. This name is inherited from the Java version and the name - will be changed in the RC1 release to avoid confusion since a common - naming convention when creating classes are .NET attributes is to - put the word 'Attribute' in the name. - - - Which one of the many options available should you choose for your - development? That depends, each one has it own set of pro's and con's - which will be discussed in turn in the following sections. - - With the transactional AOP proxy now created we can discuss the - flow of events in the code as proxied methods are invoked. When the - method is invoked, before calling the target object's method, a - transaction is created if one hasn't already been created. Then the - target method is invoked. If there was an exception throw, the - transaction is typically rolled back, but it can also be committed if - the exception type specified in the transaction option, NoRollbackFor, - matches the thrown exception. If no exception was thrown, that is taken - as a sign of success and the transaction is committed. - - When using other AOP advice with the transactional advice you can - set the order of the 'interceptor chain' so that, for example, - performance monitoring advice always precede the transactional - advice. - A First Example + Example of declarative transaction implementation Consider the following interface. The intent is to convey the concepts to you so you can concentrate on the transaction usage and not - have to worry about domain specific details. The - ITestObjectManager is a poor-mans business service - layer - the implementation of which will make two DAO calls. Clearly - this example is overly simplistic from the service layer perspective as - there isn't any business logic at all!. The 'service' interface is shown - below. + have to worry about domain specific details. + + + A QuickStart application for declarative transaction management + is included in the Spring.NET distribution and is decribed here. + + + The ITestObjectManager is a poor-mans business + service layer - the implementation of which will make two DAO calls. + Clearly this example is overly simplistic from the service layer + perspective as there isn't any business logic at all!. The 'service' + interface is shown below. public interface ITestObjectManager { @@ -657,14 +573,14 @@ // Fields/Properties ommited - [Transaction()] + [Transaction] public void SaveTwoTestObjects(TestObject to1, TestObject to2) { TestObjectDao.Create(to1.Name, to1.Age); TestObjectDao.Create(to2.Name, to1.Age); } - [Transaction()] + [Transaction] public void DeleteTwoTestObjects(string name1, string name2) { TestObjectDao.Delete(name1); @@ -773,8 +689,7 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); <!-- The object that performs multiple data access operations --> - <object id="testObjectManager" - type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests"> + <object id="testObjectManager" type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests"> <property name="TestObjectDao" ref="testObjectDao"/> </object> @@ -783,144 +698,10 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); This is standard Spring configuration and as such provides you with the flexibility to parameterize your connection string and to - easily switch implementations of your DAO objects. The configuration to - create a transactional proxy for the manager class is shown - below. + easily switch implementations of your DAO objects. - <!-- 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 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 following section shows how to configure the declarative + transactions using Spring's transaction namespace. @@ -1554,13 +1335,12 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); if you choose not to use the transaction namespace for declarative transaction management then you can use 'lower level' object definitions - to configure declarative transactions. This approach was shown in the - first example. The use of - Spring's autoproxy functionality defines criteria to select a collection - of objects to create a transactional AOP proxy. There are two AutoProxy - classes that you can use, ObjectNameAutoProxyCreator - and DefaultAdvisorAutoProxyCreator. If you are using - the new transaction namespace support you do not need to configure these + to configure declarative transactions. The use of Spring's autoproxy + functionality defines criteria to select a collection of objects to + create a transactional AOP proxy. There are two AutoProxy classes that + you can use, ObjectNameAutoProxyCreator and + DefaultAdvisorAutoProxyCreator. If you are using the + new transaction namespace support you do not need to configure these objects as a DefaultAdvisorAutoProxyCreator is created 'under the covers' while parsing the transaction namespace elements @@ -1593,219 +1373,11 @@ mgr.DeleteTwoTestObjects("Jack", "Jill"); Creating transactional proxies with DefaultAdvisorAutoProxyCreator - This is a commonly used way to configure declarative - transactions since it 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. An example of this - configuration approach was shown in Chapter 5. + This is not longer a common way to configure declarative + transactions but is discussed in the "Classic Spring" appendiex here. - - - 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. - - - - 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> - - - - 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. -