spring-framework/spring-framework-reference/src/transaction.xml

2392 lines
112 KiB
XML
Raw Normal View History

2009-04-15 05:37:40 +08:00
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="transaction">
<title>Transaction management</title>
<section id="transaction-intro">
<title>Introduction</title>
<para>One of the most compelling reasons to use the Spring Framework is
the comprehensive transaction support. The Spring Framework provides a
consistent abstraction for transaction management that delivers the
following benefits:</para>
<itemizedlist>
<listitem>
<para>Provides a consistent programming model across different
transaction APIs such as JTA, JDBC, Hibernate, JPA, and JDO.</para>
</listitem>
<listitem>
<para>Supports <link linkend="transaction-declarative">declarative
transaction management</link>.</para>
</listitem>
<listitem>
<para>Provides a simpler API for <link
linkend="transaction-programmatic">programmatic</link> transaction
management than a number of complex transaction APIs such as
JTA.</para>
</listitem>
<listitem>
<para>Integrates very well with Spring's various data access
abstractions.</para>
</listitem>
</itemizedlist>
<para>This chapter is divided up into a number of sections, each detailing
one of the value-adds or technologies of the Spring Framework's
transaction support. The chapter closes up with some discussion of best
practices surrounding transaction management (for example, choosing
between declarative and programmatic transaction management).</para>
<itemizedlist>
<listitem>
<para>The first section, entitled <link
linkend="transaction-motivation">Motivations</link>, describes
<emphasis>why</emphasis> one would want to use the Spring Framework's
transaction abstraction as opposed to EJB CMT or driving transactions
via a proprietary API such as Hibernate.</para>
</listitem>
<listitem>
<para>The second section, entitled <link
linkend="transaction-strategies">Key abstractions</link> outlines the
core classes in the Spring Framework's transaction support, as well as
how to configure and obtain <interfacename>DataSource</interfacename>
instances from a variety of sources.</para>
</listitem>
<listitem>
<para>The third section, entitled <link
linkend="transaction-declarative">Declarative transaction
management</link>, covers the Spring Framework's support for
declarative transaction management.</para>
</listitem>
<listitem>
<para>The fourth section, entitled <link
linkend="transaction-programmatic">Programmatic transaction
management</link>, covers the Spring Framework's support for
programmatic (that is, explicitly coded) transaction
management.</para>
</listitem>
</itemizedlist>
</section>
<section id="transaction-motivation">
<title>Motivations</title>
<sidebar>
<title>Is an application server needed for transaction
management?</title>
<para>The Spring Framework's transaction management support
significantly changes traditional thinking as to when a J2EE application
requires an application server.</para>
<para>In particular, you don't need an application server just to have
declarative transactions via EJB. In fact, even if you have an
application server with powerful JTA capabilities, you may well decide
that the Spring Framework's declarative transactions offer more power
and a much more productive programming model than EJB CMT.</para>
<para>Typically you need an application server's JTA capability only if
you need to enlist multiple transactional resources, and for many
applications being able to handle transactions across multiple resources
isn't a requirement. For example, many high-end applications use a
single, highly scalable database (such as Oracle 9i RAC). Standalone
transaction managers such as <ulink
url="http://www.atomikos.com/">Atomikos Transactions</ulink> and <ulink
url="http://jotm.objectweb.org/">JOTM</ulink> are other options. (Of
course you may need other application server capabilities such as JMS
and JCA.)</para>
<para>The most important point is that with the Spring Framework
<emphasis>you can choose when to scale your application up to a
full-blown application server</emphasis>. Gone are the days when the
only alternative to using EJB CMT or JTA was to write code using local
transactions such as those on JDBC connections, and face a hefty rework
if you ever needed that code to run within global, container-managed
transactions. With the Spring Framework, only configuration needs to
change so that your code doesn't have to.</para>
</sidebar>
<para>Traditionally, J2EE developers have had two choices for transaction
management: <emphasis>global</emphasis> or <emphasis>local</emphasis>
transactions. Global transactions are managed by the application server,
using the Java Transaction API (JTA). Local transactions are
resource-specific: the most common example would be a transaction
associated with a JDBC connection. This choice has profound implications.
For instance, global transactions provide the ability to work with
multiple transactional resources (typically relational databases and
message queues). With local transactions, the application server is not
involved in transaction management and cannot help ensure correctness
across multiple resources. (It is worth noting that most applications use
a single transaction resource.)</para>
<formalpara>
<title>Global Transactions</title>
<para>Global transactions have a significant downside, in that code
needs to use JTA, and JTA is a cumbersome API to use (partly due to its
exception model). Furthermore, a JTA
<interfacename>UserTransaction</interfacename> normally needs to be
sourced from JNDI: meaning that we need to use <emphasis>both</emphasis>
JNDI <emphasis>and</emphasis> JTA to use JTA. Obviously all use of
global transactions limits the reusability of application code, as JTA
is normally only available in an application server environment.</para>
<para>Previously, the preferred way to use global transactions was via
EJB <emphasis>CMT</emphasis> (<emphasis>Container Managed
Transaction</emphasis>): CMT is a form of <emphasis
role="bold">declarative transaction management</emphasis> (as
distinguished from <emphasis role="bold">programmatic transaction
management</emphasis>). EJB CMT removes the need for transaction-related
JNDI lookups - although of course the use of EJB itself necessitates the
use of JNDI. It removes most of the need (although not entirely) to
write Java code to control transactions. The significant downside is
that CMT is tied to JTA and an application server environment. Also, it
is only available if one chooses to implement business logic in EJBs, or
at least behind a transactional EJB facade. The negatives around EJB in
general are so great that this is not an attractive proposition,
especially in the face of compelling alternatives for declarative
transaction management.</para>
</formalpara>
<formalpara>
<title>Local Transactions</title>
<para>Local transactions may be easier to use, but have significant
disadvantages: they cannot work across multiple transactional resources.
For example, code that manages transactions using a JDBC connection
cannot run within a global JTA transaction. Another downside is that
local transactions tend to be invasive to the programming model.</para>
</formalpara>
<para>Spring resolves these problems. It enables application developers to
use a <emphasis>consistent</emphasis> programming model <emphasis>in any
environment</emphasis>. You write your code once, and it can benefit from
different transaction management strategies in different environments. The
Spring Framework provides both declarative and programmatic transaction
management. Declarative transaction management is preferred by most users,
and is recommended in most cases.</para>
<para>With programmatic transaction management, developers work with the
Spring Framework transaction abstraction, which can run over any
underlying transaction infrastructure. With the preferred declarative
model, developers typically write little or no code related to transaction
management, and hence don't depend on the Spring Framework's transaction
API (or indeed on any other transaction API).</para>
</section>
<section id="transaction-strategies">
<title>Key abstractions</title>
<para>The key to the Spring transaction abstraction is the notion of a
<emphasis>transaction strategy</emphasis>. A transaction strategy is
defined by the
<interfacename>org.springframework.transaction.PlatformTransactionManager</interfacename>
interface, shown below:</para>
<programlisting language="java">public interface PlatformTransactionManager {
TransactionStatus getTransaction(TransactionDefinition definition)
throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}</programlisting>
<para>This is primarily an SPI interface, although it can be used <link
linkend="transaction-programmatic-ptm">programmatically</link>. Note that
in keeping with the Spring Framework's philosophy,
<interfacename>PlatformTransactionManager</interfacename> is an
<emphasis>interface</emphasis>, and can thus be easily mocked or stubbed
as necessary. Nor is it tied to a lookup strategy such as JNDI:
<interfacename>PlatformTransactionManager</interfacename> implementations
are defined like any other object (or bean) in the Spring Framework's IoC
container. This benefit alone makes it a worthwhile abstraction even when
working with JTA: transactional code can be tested much more easily than
if it used JTA directly.</para>
<para>Again in keeping with Spring's philosophy, the
<exceptionname>TransactionException</exceptionname> that can be thrown by
any of the <interfacename>PlatformTransactionManager</interfacename>
interface's methods is <emphasis>unchecked</emphasis> (that is it extends
the <exceptionname>java.lang.RuntimeException</exceptionname> class).
Transaction infrastructure failures are almost invariably fatal. In rare
cases where application code can actually recover from a transaction
failure, the application developer can still choose to catch and handle
<exceptionname>TransactionException</exceptionname>. The salient point is
that developers are not <emphasis>forced</emphasis> to do so.</para>
<para>The <methodname>getTransaction(..)</methodname> method returns a
<interfacename>TransactionStatus</interfacename> object, depending on a
<interfacename>TransactionDefinition</interfacename> parameter. The
returned <interfacename>TransactionStatus</interfacename> might represent
a new or existing transaction (if there were a matching transaction in the
current call stack - with the implication being that (as with J2EE
transaction contexts) a <interfacename>TransactionStatus</interfacename>
is associated with a <emphasis role="bold">thread</emphasis> of
execution).</para>
<para>The <interfacename>TransactionDefinition</interfacename> interface
specifies:</para>
<itemizedlist>
<listitem>
<para><emphasis role="bold">Isolation</emphasis>: the degree of
isolation this transaction has from the work of other transactions.
For example, can this transaction see uncommitted writes from other
transactions?</para>
</listitem>
<listitem>
<para><emphasis role="bold">Propagation</emphasis>: normally all code
executed within a transaction scope will run in that transaction.
However, there are several options specifying behavior if a
transactional method is executed when a transaction context already
exists: for example, simply continue running in the existing
transaction (the common case); or suspending the existing transaction
and creating a new transaction. <emphasis>Spring offers all of the
transaction propagation options familiar from EJB CMT</emphasis>.
(Some details regarding the semantics of transaction propagation in
Spring can be found in the section entitled <xref
linkend="tx-propagation" />.</para>
</listitem>
<listitem>
<para><emphasis role="bold">Timeout</emphasis>: how long this
transaction may run before timing out (and automatically being rolled
back by the underlying transaction infrastructure).</para>
</listitem>
<listitem>
<para><emphasis role="bold">Read-only status</emphasis>: a read-only
transaction does not modify any data. Read-only transactions can be a
useful optimization in some cases (such as when using
Hibernate).</para>
</listitem>
</itemizedlist>
<para>These settings reflect standard transactional concepts. If
necessary, please refer to a resource discussing transaction isolation
levels and other core transaction concepts because understanding such core
concepts is essential to using the Spring Framework or indeed any other
transaction management solution.</para>
<para>The <interfacename>TransactionStatus</interfacename> interface
provides a simple way for transactional code to control transaction
execution and query transaction status. The concepts should be familiar,
as they are common to all transaction APIs:</para>
<programlisting language="java">public interface TransactionStatus extends SavepointManager {
2009-05-16 04:15:18 +08:00
boolean isNewTransaction();
2009-05-16 04:15:18 +08:00
boolean hasSavepoint();
void setRollbackOnly();
boolean isRollbackOnly();
void flush();
boolean isCompleted();
}</programlisting>
<para>Regardless of whether you opt for declarative or programmatic
transaction management in Spring, defining the correct
<interfacename>PlatformTransactionManager</interfacename> implementation
is absolutely essential. In good Spring fashion, this important definition
typically is made using via Dependency Injection.</para>
<para><interfacename>PlatformTransactionManager</interfacename>
implementations normally require knowledge of the environment in which
they work: JDBC, JTA, Hibernate, etc The following examples from the
<literal>dataAccessContext-local.xml</literal> file from Spring's
<emphasis role="bold">jPetStore</emphasis> sample application show how a
local <interfacename>PlatformTransactionManager</interfacename>
implementation can be defined. (This will work with plain JDBC.)</para>
<para>We must define a JDBC <interfacename>DataSource</interfacename>, and
then use the Spring <classname>DataSourceTransactionManager</classname>,
giving it a reference to the
<interfacename>DataSource</interfacename>.</para>
<programlisting language="xml">&lt;bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"&gt;
&lt;property name="driverClassName" value="${jdbc.driverClassName}" /&gt;
&lt;property name="url" value="${jdbc.url}" /&gt;
&lt;property name="username" value="${jdbc.username}" /&gt;
&lt;property name="password" value="${jdbc.password}" /&gt;
&lt;/bean&gt;</programlisting>
<para>The related
<interfacename>PlatformTransactionManager</interfacename> bean definition
will look like this:</para>
<programlisting language="xml">&lt;bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"&gt;
&lt;property name="dataSource" ref="dataSource"/&gt;
&lt;/bean&gt;</programlisting>
<para>If we use JTA in a J2EE container, as in the
<filename>'dataAccessContext-jta.xml'</filename> file from the same sample
application, we use a container <interfacename>DataSource</interfacename>,
obtained via JNDI, in conjunction with Spring's
<classname>JtaTransactionManager</classname>. The
<classname>JtaTransactionManager</classname> doesn't need to know about
the <interfacename>DataSource</interfacename>, or any other specific
resources, as it will use the container's global transaction management
infrastructure.</para>
<programlisting language="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
2009-05-16 04:15:18 +08:00
xsi:schemaLocation="
http://www.springframework.org/schema/beans
2009-06-10 07:39:37 +08:00
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
2009-05-16 04:15:18 +08:00
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-3.0.xsd"&gt;
&lt;jee:jndi-lookup id="dataSource" jndi-name="jdbc/jpetstore"/&gt;
&lt;bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" /&gt;
<lineannotation>&lt;!-- other <literal>&lt;bean/&gt;</literal> definitions here --&gt;</lineannotation>
&lt;/beans&gt;</programlisting>
<note>
<para>The above definition of the <literal>'dataSource'</literal> bean
uses the <literal>&lt;jndi-lookup/&gt;</literal> tag from the
<literal>'jee'</literal> namespace. For more information on schema-based
configuration, see <xref linkend="xsd-config" />, and for more
information on the <literal>&lt;jee/&gt;</literal> tags see the section
entitled <xref linkend="xsd-config-body-schemas-jee" />.</para>
</note>
<para>We can also use Hibernate local transactions easily, as shown in the
following examples from the Spring Framework's <emphasis
role="bold">PetClinic</emphasis> sample application. In this case, we need
to define a Hibernate <classname>LocalSessionFactoryBean</classname>,
which application code will use to obtain Hibernate
<interfacename>Session</interfacename> instances.</para>
<para>The <interfacename>DataSource</interfacename> bean definition will
be similar to the one shown previously (and thus is not shown). If the
<interfacename>DataSource</interfacename> is managed by the JEE container
it should be non-transactional as the Spring Framework, rather than the
JEE container, will manage transactions.</para>
<para>The <literal>'txManager'</literal> bean in this case is of the
<classname>HibernateTransactionManager</classname> type. In the same way
as the <classname>DataSourceTransactionManager</classname> needs a
reference to the <interfacename>DataSource</interfacename>, the
<classname>HibernateTransactionManager</classname> needs a reference to
the <interfacename>SessionFactory</interfacename>.</para>
<programlisting language="xml">&lt;bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"&gt;
&lt;property name="dataSource" ref="dataSource" /&gt;
&lt;property name="mappingResources"&gt;
&lt;list&gt;
&lt;value&gt;org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;property name="hibernateProperties"&gt;
&lt;value&gt;
hibernate.dialect=${hibernate.dialect}
&lt;/value&gt;
&lt;/property&gt;
&lt;/bean&gt;
&lt;bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"&gt;
&lt;property name="sessionFactory" ref="sessionFactory" /&gt;
&lt;/bean&gt;</programlisting>
<para>With Hibernate and JTA transactions, we can simply use the
<classname>JtaTransactionManager</classname> as with JDBC or any other
resource strategy.</para>
<programlisting language="xml">&lt;bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/&gt;</programlisting>
<para>Note that this is identical to JTA configuration for any resource,
as these are global transactions, which can enlist any transactional
resource.</para>
<remark><para>In all these cases, application code will not need to change
at all. We can change how transactions are managed merely by changing
configuration, even if that change means moving from local to global
transactions or vice versa.</para></remark>
</section>
<section id="tx-resource-synchronization">
<title>Resource synchronization with transactions</title>
<para>It should now be clear how different transaction managers are
created, and how they are linked to related resources which need to be
synchronized to transactions (for example
<classname>DataSourceTransactionManager</classname> to a JDBC
<interfacename>DataSource</interfacename>,
<classname>HibernateTransactionManager</classname> to a Hibernate
<interfacename>SessionFactory</interfacename>, and so forth). There
remains the question however of how the application code, directly or
indirectly using a persistence API (such as JDBC, Hibernate, and JDO),
ensures that these resources are obtained and handled properly in terms of
proper creation/reuse/cleanup and trigger (optionally) transaction
synchronization via the relevant
<interfacename>PlatformTransactionManager</interfacename>.</para>
<section id="tx-resource-synchronization-high">
<title>High-level approach</title>
<para>The preferred approach is to use Spring's highest level
persistence integration APIs. These do not replace the native APIs, but
internally handle resource creation/reuse, cleanup, optional transaction
synchronization of the resources and exception mapping so that user data
access code doesn't have to worry about these concerns at all, but can
concentrate purely on non-boilerplate persistence logic. Generally, the
same <emphasis>template</emphasis> approach is used for all persistence
APIs, with examples including the <classname>JdbcTemplate</classname>,
<classname>HibernateTemplate</classname>, and
<classname>JdoTemplate</classname> classes (detailed in subsequent
chapters of this reference documentation.</para>
</section>
<section id="tx-resource-synchronization-low">
<title>Low-level approach</title>
<para>At a lower level exist classes such as
<classname>DataSourceUtils</classname> (for JDBC),
<classname>SessionFactoryUtils</classname> (for Hibernate),
<classname>PersistenceManagerFactoryUtils</classname> (for JDO), and so
on. When it is preferable for application code to deal directly with the
resource types of the native persistence APIs, these classes ensure that
proper Spring Framework-managed instances are obtained, transactions are
(optionally) synchronized, and exceptions which happen in the process
are properly mapped to a consistent API.</para>
<para>For example, in the case of JDBC, instead of the traditional JDBC
approach of calling the <literal>getConnection()</literal> method on the
<interfacename>DataSource</interfacename>, you would instead use
Spring's
<classname>org.springframework.jdbc.datasource.DataSourceUtils</classname>
class as follows:</para>
<programlisting language="java">Connection conn = DataSourceUtils.getConnection(dataSource);</programlisting>
<para>If an existing transaction exists, and already has a connection
synchronized (linked) to it, that instance will be returned. Otherwise,
the method call will trigger the creation of a new connection, which
will be (optionally) synchronized to any existing transaction, and made
available for subsequent reuse in that same transaction. As mentioned,
this has the added advantage that any
<exceptionname>SQLException</exceptionname> will be wrapped in a Spring
Framework
<exceptionname>CannotGetJdbcConnectionException</exceptionname> - one of
the Spring Framework's hierarchy of unchecked DataAccessExceptions. This
gives you more information than can easily be obtained from the
<exceptionname>SQLException</exceptionname>, and ensures portability
across databases: even across different persistence technologies.</para>
<para>It should be noted that this will also work fine without Spring
transaction management (transaction synchronization is optional), so you
can use it whether or not you are using Spring for transaction
management.</para>
<para>Of course, once you've used Spring's JDBC support or Hibernate
support, you will generally prefer not to use
<classname>DataSourceUtils</classname> or the other helper classes,
because you'll be much happier working via the Spring abstraction than
directly with the relevant APIs. For example, if you use the Spring
<classname>JdbcTemplate</classname> or <literal>jdbc.object</literal>
package to simplify your use of JDBC, correct connection retrieval
happens behind the scenes and you won't need to write any special
code.</para>
</section>
<section id="tx-resource-synchronization-tadsp">
<title><classname>TransactionAwareDataSourceProxy</classname></title>
<para>At the very lowest level exists the
<classname>TransactionAwareDataSourceProxy</classname> class. This is a
proxy for a target <interfacename>DataSource</interfacename>, which
wraps the target <interfacename>DataSource</interfacename> to add
awareness of Spring-managed transactions. In this respect, it is similar
to a transactional JNDI <interfacename>DataSource</interfacename> as
provided by a J2EE server.</para>
<para>It should almost never be necessary or desirable to use this
class, except when existing code exists which must be called and passed
a standard JDBC <interfacename>DataSource</interfacename> interface
implementation. In that case, it's possible to still have this code be
usable, but participating in Spring managed transactions. It is
preferable to write your new code using the higher level abstractions
mentioned above.</para>
</section>
</section>
<section id="transaction-declarative">
<title>Declarative transaction management</title>
<remark>Most users of the Spring Framework choose declarative transaction
management. It is the option with the least impact on application code,
and hence is most consistent with the ideals of a
<emphasis>non-invasive</emphasis> lightweight container.</remark>
<para>The Spring Framework's declarative transaction management is made
possible with Spring AOP, although, as the transactional aspects code
comes with the Spring Framework distribution and may be used in a
boilerplate fashion, AOP concepts do not generally have to be understood
to make effective use of this code.</para>
<para>It may be helpful to begin by considering EJB CMT and explaining the
similarities and differences with the Spring Framework's declarative
transaction management. The basic approach is similar: it is possible to
specify transaction behavior (or lack of it) down to individual method
level. It is possible to make a <methodname>setRollbackOnly()</methodname>
call within a transaction context if necessary. The differences
are:</para>
<itemizedlist>
<listitem>
<para>Unlike EJB CMT, which is tied to JTA, the Spring Framework's
declarative transaction management works in any environment. It can
work with JDBC, JDO, Hibernate or other transactions under the covers,
with configuration changes only.</para>
</listitem>
<listitem>
<para>The Spring Framework enables declarative transaction management
to be applied to any class, not merely special classes such as
EJBs.</para>
</listitem>
<listitem>
<para>The Spring Framework offers declarative <link
linkend="transaction-declarative-rolling-back"><emphasis>rollback
rules</emphasis>:</link> this is a feature with no EJB equivalent.
Both programmatic and declarative support for rollback rules is
provided.</para>
</listitem>
<listitem>
<para>The Spring Framework gives you an opportunity to customize
transactional behavior, using AOP. For example, if you want to insert
custom behavior in the case of transaction rollback, you can. You can
also add arbitrary advice, along with the transactional advice. With
EJB CMT, you have no way to influence the container's transaction
management other than
<methodname>setRollbackOnly()</methodname>.</para>
</listitem>
<listitem>
<para>The Spring Framework does not support propagation of transaction
contexts across remote calls, as do high-end application servers. If
you need this feature, we recommend that you use EJB. However,
consider carefully before using such a feature, because normally, one
does not want transactions to span remote calls.</para>
</listitem>
</itemizedlist>
<sidebar>
<title>Where is
<classname>TransactionProxyFactoryBean</classname>?</title>
<para>Declarative transaction configuration in versions of Spring 2.0
and above differs considerably from previous versions of Spring. The
main difference is that there is no longer any need to configure
<classname>TransactionProxyFactoryBean</classname> beans.</para>
<para>The old, pre-Spring 2.0 configuration style is still 100% valid
configuration; think of the new <literal>&lt;tx:tags/&gt;</literal> as
simply defining <classname>TransactionProxyFactoryBean</classname> beans
on your behalf.</para>
</sidebar>
<para>The concept of rollback rules is important: they enable us to
specify which exceptions (and throwables) should cause automatic roll
back. We specify this declaratively, in configuration, not in Java code.
So, while we can still call <methodname>setRollbackOnly()</methodname>on
the <interfacename>TransactionStatus</interfacename> object to roll the
current transaction back programmatically, most often we can specify a
rule that <exceptionname>MyApplicationException</exceptionname> must
always result in rollback. This has the significant advantage that
business objects don't need to depend on the transaction infrastructure.
For example, they typically don't need to import any Spring APIs,
transaction or other.</para>
<para>While the EJB default behavior is for the EJB container to
automatically roll back the transaction on a <emphasis>system
exception</emphasis> (usually a runtime exception), EJB CMT does not roll
back the transaction automatically on an <emphasis>application
exception</emphasis> (that is, a checked exception other than
<exceptionname>java.rmi.RemoteException</exceptionname>). While the Spring
default behavior for declarative transaction management follows EJB
convention (roll back is automatic only on unchecked exceptions), it is
often useful to customize this.</para>
<section id="tx-decl-explained">
<title>Understanding the Spring Framework's declarative transaction
implementation</title>
<para>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 simply to tell you to
annotate your classes with the
<interfacename>@Transactional</interfacename> annotation, add the line
(<literal>'&lt;tx:annotation-driven/&gt;'</literal>) to your
configuration, and then expect you to understand how it all works. This
section will explain the inner workings of the Spring Framework's
declarative transaction infrastructure to help you navigate your way
back upstream to calmer waters in the event of transaction-related
issues.</para>
<para>The most important concepts to grasp with regard to the Spring
Framework's declarative transaction support are that this support is
enabled <link linkend="aop-understanding-aop-proxies"><emphasis>via AOP
proxies</emphasis></link>, and that the transactional advice is driven
by <emphasis>metadata</emphasis> (currently XML- or annotation-based).
The combination of AOP with transactional metadata yields an AOP proxy
that uses a <classname>TransactionInterceptor</classname> in conjunction
with an appropriate <classname>PlatformTransactionManager</classname>
implementation to drive transactions <emphasis>around method
invocations</emphasis>.</para>
<note>
<para>Although knowledge of Spring AOP is not required to use Spring's
declarative transaction support, it can help. Spring AOP is thoroughly
covered in the chapter entitled <xref linkend="aop" />.</para>
</note>
<para>Conceptually, calling a method on a transactional proxy looks like
this...</para>
<para><mediaobject>
<imageobject role="fo">
<imagedata align="center" fileref="images/tx.png" format="PNG" />
</imageobject>
<imageobject role="html">
<imagedata align="center" fileref="images/tx.png" format="PNG" />
</imageobject>
</mediaobject></para>
</section>
<section id="transaction-declarative-first-example">
<title>A first example</title>
<para>Consider the following interface, and its attendant
implementation. (The intent is to convey the concepts, and using the
rote <classname>Foo</classname> and <classname>Bar</classname> tropes
means that you can concentrate on the transaction usage and not have to
worry about the domain model.)</para>
<programlisting language="java"><lineannotation>// the service interface that we want to make transactional</lineannotation>
package x.y.service;
public interface FooService {
Foo getFoo(String fooName);
Foo getFoo(String fooName, String barName);
void insertFoo(Foo foo);
void updateFoo(Foo foo);
}</programlisting>
<programlisting language="java"><lineannotation>// an implementation of the above interface</lineannotation>
package x.y.service;
public class DefaultFooService implements FooService {
public Foo getFoo(String fooName) {
throw new UnsupportedOperationException();
}
public Foo getFoo(String fooName, String barName) {
throw new UnsupportedOperationException();
}
public void insertFoo(Foo foo) {
throw new UnsupportedOperationException();
}
public void updateFoo(Foo foo) {
throw new UnsupportedOperationException();
}
}</programlisting>
<para><emphasis>(For the purposes of this example, the fact that the
<classname>DefaultFooService</classname> class throws
<exceptionname>UnsupportedOperationException</exceptionname> instances
in the body of each implemented method is good; it will allow us to see
transactions being created and then rolled back in response to the
<exceptionname>UnsupportedOperationException</exceptionname> instance
being thrown.)</emphasis></para>
<para>Let's assume that the first two methods of the
<interfacename>FooService</interfacename> interface
(<literal>getFoo(String)</literal> and <literal>getFoo(String,
String)</literal>) have to execute in the context of a transaction with
read-only semantics, and that the other methods
(<literal>insertFoo(Foo)</literal> and
<literal>updateFoo(Foo)</literal>) have to execute in the context of a
transaction with read-write semantics. Don't worry about taking the
following configuration in all at once; everything will be explained in
detail in the next few paragraphs.</para>
<programlisting language="xml"><lineannotation>&lt;!-- from the file <literal>'context.xml'</literal> --&gt;</lineannotation>
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
<lineannotation><emphasis role="bold">xmlns:tx="http://www.springframework.org/schema/tx"</emphasis></lineannotation>
xsi:schemaLocation="
2009-05-16 04:15:18 +08:00
http://www.springframework.org/schema/beans
2009-06-10 07:39:37 +08:00
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
<lineannotation><emphasis role="bold">http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd</emphasis></lineannotation>
2009-05-16 04:15:18 +08:00
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"&gt;
<lineannotation>&lt;!-- this is the service object that we want to make transactional --&gt;</lineannotation>
&lt;bean id="fooService" class="x.y.service.DefaultFooService"/&gt;
<lineannotation>&lt;!-- the transactional advice (what 'happens'; see the <literal>&lt;aop:advisor/&gt;</literal> bean below) --&gt;</lineannotation>
&lt;tx:advice id="txAdvice" transaction-manager="txManager"&gt;
<lineannotation>&lt;!-- the transactional semantics... --&gt;</lineannotation>
&lt;tx:attributes&gt;
<lineannotation>&lt;!-- all methods starting with <literal>'get'</literal> are read-only --&gt;</lineannotation>
&lt;tx:method name="get*" read-only="true"/&gt;
<lineannotation>&lt;!-- other methods use the default transaction settings (see below) --&gt;</lineannotation>
&lt;tx:method name="*"/&gt;
&lt;/tx:attributes&gt;
&lt;/tx:advice&gt;
<lineannotation>&lt;!-- ensure that the above transactional advice runs for any execution
of an operation defined by the <interfacename>FooService</interfacename> interface --&gt;</lineannotation>
&lt;aop:config&gt;
&lt;aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/&gt;
&lt;aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/&gt;
&lt;/aop:config&gt;
<lineannotation>&lt;!-- don't forget the <interfacename>DataSource</interfacename> --&gt;</lineannotation>
&lt;bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"&gt;
&lt;property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/&gt;
&lt;property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/&gt;
&lt;property name="username" value="scott"/&gt;
&lt;property name="password" value="tiger"/&gt;
&lt;/bean&gt;
<lineannotation>&lt;!-- similarly, don't forget the <interfacename>PlatformTransactionManager</interfacename> --&gt;</lineannotation>
&lt;bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"&gt;
&lt;property name="dataSource" ref="dataSource"/&gt;
&lt;/bean&gt;
<lineannotation>&lt;!-- other <literal>&lt;bean/&gt;</literal> definitions here --&gt;</lineannotation>
&lt;/beans&gt;</programlisting>
<para>Let's pick apart the above configuration. We have a service object
(the <literal>'fooService'</literal> bean) that we want to make
transactional. The transaction semantics that we want to apply are
encapsulated in the <literal>&lt;tx:advice/&gt;</literal> definition.
The <literal>&lt;tx:advice/&gt;</literal> definition reads as
<quote><emphasis>... all methods on starting with
<literal>'get'</literal> are to execute in the context of a read-only
transaction, and all other methods are to execute with the default
transaction semantics</emphasis></quote>. The
<literal>'transaction-manager'</literal> attribute of the
<literal>&lt;tx:advice/&gt;</literal> tag is set to the name of the
<interfacename>PlatformTransactionManager</interfacename> bean that is
going to actually <emphasis>drive</emphasis> the transactions (in this
case the <literal>'txManager'</literal> bean).</para>
<tip>
<para>You can actually omit the
<literal>'transaction-manager'</literal> attribute in the
transactional advice (<literal>&lt;tx:advice/&gt;</literal>) if the
bean name of the
<interfacename>PlatformTransactionManager</interfacename> that you
want to wire in has the name <literal>'transactionManager'</literal>.
If the <interfacename>PlatformTransactionManager</interfacename> bean
that you want to wire in has any other name, then you have to be
explicit and use the <literal>'transaction-manager'</literal>
attribute as in the example above.</para>
</tip>
<para>The <literal>&lt;aop:config/&gt;</literal> definition ensures that
the transactional advice defined by the <literal>'txAdvice'</literal>
bean actually executes at the appropriate points in the program. First
we define a pointcut that matches the execution of any operation defined
in the <interfacename>FooService</interfacename> interface
(<literal>'fooServiceOperation'</literal>). Then we associate the
pointcut with the <literal>'txAdvice'</literal> using an advisor. The
result indicates that at the execution of a
<literal>'fooServiceOperation'</literal>, the advice defined by
<literal>'txAdvice'</literal> will be run.</para>
<para>The expression defined within the
<literal>&lt;aop:pointcut/&gt;</literal> element is an AspectJ pointcut
expression; see the chapter entitled <xref linkend="aop" /> for more
details on pointcut expressions in Spring 2.0.</para>
<para>A common requirement is to make an entire service layer
transactional. The best way to do this is simply to change the pointcut
expression to match any operation in your service layer. For
example:</para>
<programlisting language="xml">&lt;aop:config&gt;
&lt;aop:pointcut id="fooServiceMethods" expression="execution(* x.y.service.*.*(..))"/&gt;
&lt;aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceMethods"/&gt;
&lt;/aop:config&gt;</programlisting>
<para><emphasis>(This example assumes that all your service interfaces
are defined in the <literal>'x.y.service'</literal> package; see the
chapter entitled <xref linkend="aop" /> for more
details.)</emphasis></para>
<para>Now that we've analyzed the configuration, you may be asking
yourself, <quote><emphasis>Okay... but what does all this configuration
actually do?</emphasis></quote>.</para>
<para>The above configuration is going to effect the creation of a
transactional proxy around the object that is created from the
<literal>'fooService'</literal> bean definition. The proxy will be
configured with the transactional advice, so that when an appropriate
method is invoked <emphasis>on the proxy</emphasis>, a transaction
<emphasis>may</emphasis> be started, suspended, be marked as read-only,
etc., depending on the transaction configuration associated with that
method. Consider the following program that test drives the above
configuration.</para>
<programlisting language="java">public final class Boot {
public static void main(final String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml", Boot.class);
FooService fooService = (FooService) ctx.getBean("fooService");
fooService.insertFoo (new Foo());
}
}</programlisting>
<para>The output from running the above program will look something like
this. <emphasis>(Please note that the Log4J output and the stacktrace
from the <exceptionname>UnsupportedOperationException</exceptionname>
thrown by the <methodname>insertFoo(..)</methodname> method of the
<classname>DefaultFooService</classname> class have been truncated in
the interest of clarity.)</emphasis></para>
<programlisting language="xml"> <lineannotation><emphasis role="bold">&lt;!-- the Spring container is starting up... --&gt;</emphasis></lineannotation>
[AspectJInvocationContextExposingAdvisorAutoProxyCreator] - Creating implicit proxy
for bean 'fooService' with 0 common interceptors and 1 specific interceptors
<lineannotation><emphasis role="bold">&lt;!-- the <classname>DefaultFooService</classname> is actually proxied --&gt;</emphasis></lineannotation>
[JdkDynamicAopProxy] - Creating JDK dynamic proxy for [x.y.service.DefaultFooService]
<lineannotation><emphasis role="bold">&lt;!-- ... the <literal>insertFoo(..)</literal> method is now being invoked on the proxy --&gt;</emphasis></lineannotation>
[TransactionInterceptor] - Getting transaction for x.y.service.FooService.insertFoo
<lineannotation><emphasis role="bold">&lt;!-- the transactional advice kicks in here... --&gt;</emphasis></lineannotation>
[DataSourceTransactionManager] - Creating new transaction with name [x.y.service.FooService.insertFoo]
[DataSourceTransactionManager] - Acquired Connection
[org.apache.commons.dbcp.PoolableConnection@a53de4] for JDBC transaction
<lineannotation><emphasis role="bold">&lt;!-- the <literal>insertFoo(..)</literal> method from <classname>DefaultFooService</classname> throws an exception... --&gt;</emphasis></lineannotation>
[RuleBasedTransactionAttribute] - Applying rules to determine whether transaction should
rollback on java.lang.UnsupportedOperationException
[TransactionInterceptor] - Invoking rollback for transaction on x.y.service.FooService.insertFoo
due to throwable [java.lang.UnsupportedOperationException]
<lineannotation><emphasis role="bold">&lt;!-- and the transaction is rolled back (by default, <exceptionname>RuntimeException</exceptionname> instances cause rollback) --&gt;</emphasis></lineannotation>
[DataSourceTransactionManager] - Rolling back JDBC transaction on Connection
[org.apache.commons.dbcp.PoolableConnection@a53de4]
[DataSourceTransactionManager] - Releasing JDBC Connection after transaction
[DataSourceUtils] - Returning JDBC Connection to DataSource
Exception in thread "main" java.lang.UnsupportedOperationException
at x.y.service.DefaultFooService.insertFoo(DefaultFooService.java:14)
<lineannotation><emphasis role="bold">&lt;!-- AOP infrastructure stack trace elements removed for clarity --&gt;</emphasis></lineannotation>
at $Proxy0.insertFoo(Unknown Source)
at Boot.main(Boot.java:11)</programlisting>
</section>
<section id="transaction-declarative-rolling-back">
<title>Rolling back</title>
<para>The previous section outlined the basics of how to specify the
transactional settings for the classes, typically service layer classes,
in your application in a declarative fashion. This section describes how
you can control the rollback of transactions in a simple declarative
fashion.</para>
<para>The recommended way to indicate to the Spring Framework's
transaction infrastructure that a transaction's work is to be rolled
back is to throw an <exceptionname>Exception</exceptionname> from code
that is currently executing in the context of a transaction. The Spring
Framework's transaction infrastructure code will catch any unhandled
<exceptionname>Exception</exceptionname> as it bubbles up the call
stack, and will mark the transaction for rollback.</para>
<para>Note however that the Spring Framework's transaction
infrastructure code will, by default, <emphasis>only</emphasis> mark a
transaction for rollback in the case of runtime, unchecked exceptions;
that is, when the thrown exception is an instance or subclass of
<exceptionname>RuntimeException</exceptionname>.
(<literal>Errors</literal> will also - by default - result in a
rollback.) Checked exceptions that are thrown from a transactional
method will <emphasis>not</emphasis> result in the transaction being
rolled back.</para>
<para>Exactly which <exceptionname>Exception</exceptionname> types mark
a transaction for rollback can be configured. Find below a snippet of
XML configuration that demonstrates how one would configure rollback for
a checked, application-specific <exceptionname>Exception</exceptionname>
type.</para>
<programlisting language="xml">&lt;tx:advice id="txAdvice" transaction-manager="txManager"&gt;
&lt;tx:attributes&gt;
&lt;tx:method name="get*" read-only="true" <lineannotation><emphasis
role="bold">rollback-for="NoProductInStockException"</emphasis></lineannotation>/&gt;
&lt;tx:method name="*"/&gt;
&lt;/tx:attributes&gt;
&lt;/tx:advice&gt;</programlisting>
<para>It is also possible to specify 'no rollback rules', for those
times when you do <emphasis>not</emphasis> want a transaction to be
marked for rollback when an exception is thrown. In the example
configuration below, we effectively are telling the Spring Framework's
transaction infrastructure to commit the attendant transaction even in
the face of an unhandled
<exceptionname>InstrumentNotFoundException</exceptionname>.</para>
<programlisting language="xml">&lt;tx:advice id="txAdvice"&gt;
&lt;tx:attributes&gt;
&lt;tx:method name="updateStock" <lineannotation><emphasis role="bold">no-rollback-for="InstrumentNotFoundException"</emphasis></lineannotation>/&gt;
&lt;tx:method name="*"/&gt;
&lt;/tx:attributes&gt;
&lt;/tx:advice&gt;</programlisting>
<para>When the Spring Framework's transaction infrastructure has caught
an exception and is consulting any configured rollback rules to
determine whether or not to mark the transaction for rollback, the
<emphasis>strongest</emphasis> matching rule wins. So in the case of the
following configuration, any exception other than an
<exceptionname>InstrumentNotFoundException</exceptionname> would result
in the attendant transaction being marked for rollback.</para>
<programlisting language="xml">&lt;tx:advice id="txAdvice"&gt;
&lt;tx:attributes&gt;
&lt;tx:method name="*" rollback-for="Throwable" no-rollback-for="InstrumentNotFoundException"/&gt;
&lt;/tx:attributes&gt;
&lt;/tx:advice&gt;</programlisting>
<para>The second way to indicate that a rollback is required is to do so
<emphasis>programmatically</emphasis>. Although very simple, this way is
quite invasive, and tightly couples your code to the Spring Framework's
transaction infrastructure, as can be seen below:</para>
<programlisting language="java">public void resolvePosition() {
try {
<lineannotation>// some business logic...</lineannotation>
} catch (NoProductInStockException ex) {
<lineannotation>// trigger rollback programmatically</lineannotation>
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}</programlisting>
<para>You are strongly encouraged to use the declarative approach to
rollback if at all possible. Programmatic rollback is available should
you absolutely need it, but its usage flies in the face of achieving a
nice, clean POJO-based architecture.</para>
</section>
<section id="transaction-declarative-diff-tx">
<title>Configuring different transactional semantics for different
beans</title>
<para>Consider the scenario where you have a number of service layer
objects, and you want to apply <emphasis>totally different</emphasis>
transactional configuration to each of them. This is achieved by
defining distinct <literal>&lt;aop:advisor/&gt;</literal> elements with
differing <literal>'pointcut'</literal> and
<literal>'advice-ref'</literal> attribute values.</para>
<para>Let's assume that all of your service layer classes are defined in
a root <literal>'x.y.service'</literal> package. To make all beans that
are instances of classes defined in that package (or in subpackages) and
that have names ending in <literal>'Service'</literal> have the default
transactional configuration, you would write the following:</para>
<programlisting language="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
2009-05-16 04:15:18 +08:00
http://www.springframework.org/schema/beans
2009-06-10 07:39:37 +08:00
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
2009-05-16 04:15:18 +08:00
http://www.springframework.org/schema/tx
2009-06-10 07:39:37 +08:00
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
2009-05-16 04:15:18 +08:00
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"&gt;
&lt;aop:config&gt;
&lt;aop:pointcut id="serviceOperation"
expression="<lineannotation>execution(* x.y.service..*Service.*(..))</lineannotation>"/&gt;
&lt;aop:advisor pointcut-ref="serviceOperation" advice-ref="txAdvice"/&gt;
&lt;/aop:config&gt;
<lineannotation>&lt;!-- these two beans will be transactional... --&gt;</lineannotation>
&lt;bean id="fooService" class="x.y.service.DefaultFooService"/&gt;
&lt;bean id="barService" class="x.y.service.extras.SimpleBarService"/&gt;
<lineannotation>&lt;!-- ... and these two beans won't --&gt;</lineannotation>
&lt;bean id="anotherService" class="org.xyz.SomeService"/&gt; <lineannotation>&lt;!-- (not in the right package) --&gt;</lineannotation>
&lt;bean id="barManager" class="x.y.service.SimpleBarManager"/&gt; <lineannotation>&lt;!-- (doesn't end in 'Service') --&gt;</lineannotation>
&lt;tx:advice id="txAdvice"&gt;
&lt;tx:attributes&gt;
&lt;tx:method name="get*" read-only="true"/&gt;
&lt;tx:method name="*"/&gt;
&lt;/tx:attributes&gt;
&lt;/tx:advice&gt;
<lineannotation>&lt;!-- other transaction infrastructure beans such as a <interfacename>PlatformTransactionManager</interfacename> omitted... --&gt;</lineannotation>
&lt;/beans&gt;</programlisting>
<para>Find below an example of configuring two distinct beans with
totally different transactional settings.</para>
<programlisting language="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
2009-05-16 04:15:18 +08:00
http://www.springframework.org/schema/beans
2009-06-10 07:39:37 +08:00
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
2009-05-16 04:15:18 +08:00
http://www.springframework.org/schema/tx
2009-06-10 07:39:37 +08:00
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
2009-05-16 04:15:18 +08:00
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"&gt;
&lt;aop:config&gt;
&lt;aop:pointcut id="defaultServiceOperation"
expression="<lineannotation>execution(* x.y.service.*Service.*(..))</lineannotation>"/&gt;
&lt;aop:pointcut id="noTxServiceOperation"
expression="<lineannotation>execution(* x.y.service.ddl.DefaultDdlManager.*(..))</lineannotation>"/&gt;
&lt;aop:advisor pointcut-ref="defaultServiceOperation" advice-ref="defaultTxAdvice"/&gt;
&lt;aop:advisor pointcut-ref="noTxServiceOperation" advice-ref="noTxAdvice"/&gt;
&lt;/aop:config&gt;
<lineannotation>&lt;!-- this bean will be transactional (see the <literal>'defaultServiceOperation'</literal> pointcut) --&gt;</lineannotation>
&lt;bean id="fooService" class="x.y.service.DefaultFooService"/&gt;
<lineannotation>&lt;!-- this bean will also be transactional, but with totally different transactional settings --&gt;</lineannotation>
&lt;bean id="anotherFooService" class="x.y.service.ddl.DefaultDdlManager"/&gt;
&lt;tx:advice id="defaultTxAdvice"&gt;
&lt;tx:attributes&gt;
&lt;tx:method name="get*" read-only="true"/&gt;
&lt;tx:method name="*"/&gt;
&lt;/tx:attributes&gt;
&lt;/tx:advice&gt;
&lt;tx:advice id="noTxAdvice"&gt;
&lt;tx:attributes&gt;
&lt;tx:method name="*" propagation="NEVER"/&gt;
&lt;/tx:attributes&gt;
&lt;/tx:advice&gt;
<lineannotation>&lt;!-- other transaction infrastructure beans such as a <interfacename>PlatformTransactionManager</interfacename> omitted... --&gt;</lineannotation>
&lt;/beans&gt;</programlisting>
</section>
<section id="transaction-declarative-txadvice-settings">
<title><literal>&lt;tx:advice/&gt;</literal> settings</title>
<para>This section summarises the various transactional settings that
can be specified using the <literal>&lt;tx:advice/&gt;</literal> tag.
The default <literal>&lt;tx:advice/&gt;</literal> settings are:</para>
<para><itemizedlist>
<listitem>
<para>The <link linkend="tx-propagation">propagation
setting</link> is <literal>REQUIRED</literal></para>
</listitem>
<listitem>
<para>The isolation level is <literal>DEFAULT</literal></para>
</listitem>
<listitem>
<para>The transaction is read/write</para>
</listitem>
<listitem>
<para>The transaction timeout defaults to the default timeout of
the underlying transaction system, or or none if timeouts are not
supported</para>
</listitem>
<listitem>
<para>Any <exceptionname>RuntimeException</exceptionname> will
trigger rollback, and any checked
<exceptionname>Exception</exceptionname> will not</para>
</listitem>
</itemizedlist></para>
<para>These default settings can be changed; the various attributes of
the <literal>&lt;tx:method/&gt;</literal> tags that are nested within
<literal>&lt;tx:advice/&gt;</literal> and
<literal>&lt;tx:attributes/&gt;</literal> tags are summarized
below:</para>
<para><table id="tx-method-settings">
<title><literal>&lt;tx:method/&gt;</literal> settings</title>
<tgroup cols="4">
<thead>
<row>
<entry>Attribute</entry>
<entry>Required?</entry>
<entry>Default</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>name</literal></entry>
<entry>Yes</entry>
<entry></entry>
<entry><para>The method name(s) with which the transaction
attributes are to be associated. The wildcard (*) character
can be used to associate the same transaction attribute
settings with a number of methods; for example,
<literal>'get*'</literal>,
<literal>'handle*'</literal>,<literal> 'on*Event'</literal>,
and so forth.</para></entry>
</row>
<row>
<entry><literal>propagation</literal></entry>
<entry>No</entry>
<entry>REQUIRED</entry>
<entry>The transaction propagation behavior</entry>
</row>
<row>
<entry><literal>isolation</literal></entry>
<entry>No</entry>
<entry>DEFAULT</entry>
<entry>The transaction isolation level</entry>
</row>
<row>
<entry><literal>timeout</literal></entry>
<entry>No</entry>
<entry>-1</entry>
<entry>The transaction timeout value (in seconds)</entry>
</row>
<row>
<entry><literal>read-only</literal></entry>
<entry>No</entry>
<entry>false</entry>
<entry>Is this transaction read-only?</entry>
</row>
<row>
<entry><literal>rollback-for</literal></entry>
<entry>No</entry>
<entry></entry>
<entry><para>The <literal>Exception(s)</literal> that will
trigger rollback; comma-delimited. For example,
<literal>'com.foo.MyBusinessException,ServletException'</literal></para></entry>
</row>
<row>
<entry><literal>no-rollback-for</literal></entry>
<entry>No</entry>
<entry></entry>
<entry><para>The <literal>Exception(s)</literal> that will
<emphasis>not</emphasis> trigger rollback; comma-delimited.
For example,
<literal>'com.foo.MyBusinessException,ServletException'</literal></para></entry>
</row>
</tbody>
</tgroup>
</table></para>
</section>
<section id="transaction-declarative-annotations">
<title>Using <interfacename>@Transactional</interfacename></title>
<note>
<para>The functionality offered by the
<interfacename>@Transactional</interfacename> annotation and the
support classes is only available to you if you are using at least
Java 5 (Tiger).</para>
</note>
<para>In addition to the XML-based declarative approach to transaction
configuration, you can also use an annotation-based approach to
transaction configuration. Declaring transaction semantics directly in
the Java source code puts the declarations much closer to the affected
code, and there is generally not much danger of undue coupling, since
code that is meant to be used transactionally is almost always deployed
that way anyway.</para>
<para>The ease-of-use afforded by the use of the
<interfacename>@Transactional</interfacename> annotation is best
illustrated with an example, after which all of the details will be
explained. Consider the following class definition:</para>
<programlisting language="java"><lineannotation>// the service class that we want to make transactional</lineannotation>
<emphasis role="bold">@Transactional</emphasis>
public class DefaultFooService implements FooService {
Foo getFoo(String fooName);
Foo getFoo(String fooName, String barName);
void insertFoo(Foo foo);
void updateFoo(Foo foo);
}</programlisting>
<para>When the above POJO is defined as a bean in a Spring IoC
container, the bean instance can be made transactional by adding merely
<emphasis>one</emphasis> line of XML configuration, like so:</para>
<programlisting language="xml"><lineannotation>&lt;!-- from the file <literal>'context.xml'</literal> --&gt;</lineannotation>
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
2009-05-16 04:15:18 +08:00
http://www.springframework.org/schema/beans
2009-06-10 07:39:37 +08:00
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
2009-05-16 04:15:18 +08:00
http://www.springframework.org/schema/tx
2009-06-10 07:39:37 +08:00
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
2009-05-16 04:15:18 +08:00
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"&gt;
<lineannotation>&lt;!-- this is the service object that we want to make transactional --&gt;</lineannotation>
&lt;bean id="fooService" class="x.y.service.DefaultFooService"/&gt;
<lineannotation>&lt;!-- enable the configuration of transactional behavior based on annotations --&gt;</lineannotation>
<emphasis role="bold">&lt;tx:annotation-driven transaction-manager="txManager"/&gt;</emphasis>
<lineannotation>&lt;!-- a <interfacename>PlatformTransactionManager</interfacename> is still required --&gt;</lineannotation>
&lt;bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"&gt;
<lineannotation>&lt;!-- (this dependency is defined somewhere else) --&gt;</lineannotation>
&lt;property name="dataSource" ref="dataSource"/&gt;
&lt;/bean&gt;
<lineannotation>&lt;!-- other <literal>&lt;bean/&gt;</literal> definitions here --&gt;</lineannotation>
&lt;/beans&gt;</programlisting>
<tip>
<para>You can actually omit the
<literal>'transaction-manager'</literal> attribute in the
<literal>&lt;tx:annotation-driven/&gt;</literal> tag if the bean name
of the <interfacename>PlatformTransactionManager</interfacename> that
you want to wire in has the name
<literal>'transactionManager'</literal>. If the
<interfacename>PlatformTransactionManager</interfacename> bean that
you want to dependency inject has any other name, then you have to be
explicit and use the <literal>'transaction-manager'</literal>
attribute as in the example above.</para>
</tip>
<sidebar>
<title>Method visibility and
<interfacename>@Transactional</interfacename></title>
<para>When using proxies, the
<interfacename>@Transactional</interfacename> annotation should only
be applied to methods with <emphasis>public</emphasis> visibility. If
you do annotate protected, private or package-visible methods with the
<interfacename>@Transactional</interfacename> annotation, no error
will be raised, but the annotated method will not exhibit the
configured transactional settings. Consider the use of AspectJ (see
below) if you need to annotate non-public methods.</para>
</sidebar>
<para>The <interfacename>@Transactional</interfacename> annotation may
be placed before an interface definition, a method on an interface, a
class definition, or a <emphasis>public</emphasis> method on a class.
However, please note that the mere presence of the
<interfacename>@Transactional</interfacename> annotation is not enough
to actually turn on the transactional behavior - the
<interfacename>@Transactional</interfacename> annotation <emphasis>is
simply metadata</emphasis> that can be consumed by something that is
<interfacename>@Transactional</interfacename>-aware and that can use the
metadata to configure the appropriate beans with transactional behavior.
In the case of the above example, it is the presence of the
<literal>&lt;tx:annotation-driven/&gt;</literal> element that
<emphasis>switches on</emphasis> the transactional behavior.</para>
<para>The Spring team's recommendation is that you only annotate
concrete classes with the <interfacename>@Transactional</interfacename>
annotation, as opposed to annotating interfaces. You certainly can place
the <interfacename>@Transactional</interfacename> annotation on an
interface (or an interface method), but this will only work as you would
expect it to if you are using interface-based proxies. The fact that
annotations are <emphasis>not inherited</emphasis> means that if you are
using class-based proxies (<literal>proxy-target-class="true"</literal>)
or the weaving-based aspect (<literal>mode="aspectj"</literal>) then the
transaction settings will not be recognised by the proxying/weaving
infrastructure and the object will not be wrapped in a transactional
proxy (which would be decidedly <emphasis>bad</emphasis>). So please do
take the Spring team's advice and only annotate concrete classes (and
the methods of concrete classes) with the
<interfacename>@Transactional</interfacename> annotation.</para>
<para><emphasis>Note: In proxy mode (which is the default), only
'external' method calls coming in through the proxy will be
intercepted.</emphasis> This means that 'self-invocation', i.e. a method
within the target object calling some other method of the target object,
won't lead to an actual transaction at runtime even if the invoked
method is marked with
<interfacename>@Transactional</interfacename>!</para>
<para>Consider the use of AspectJ mode (see below) if you expect
self-invocations to be wrapped with transactions as well. In this case,
there won't be a proxy in the first place; instead, the target class
will be 'weaved' (i.e. its byte code will be modified) in order to turn
<interfacename>@Transactional</interfacename> into runtime behavior on
any kind of method.</para>
<para><table id="tx-annotation-driven-settings">
<title><literal>&lt;tx:annotation-driven/&gt;</literal>
settings</title>
<tgroup cols="3">
<thead>
<row>
<entry>Attribute</entry>
<entry>Default</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>transaction-manager</literal></entry>
<entry>transactionManager</entry>
<entry><para>The name of transaction manager to use. Only
required if the name of the transaction manager is not
<literal>transactionManager</literal>, as in the example
above.</para></entry>
</row>
<row>
<entry><literal>mode</literal></entry>
<entry>proxy</entry>
<entry><para>The default mode "proxy" will process annotated
beans to be proxied using Spring's AOP framework (following
proxy semantics, as discussed above, applying to method calls
coming in through the proxy only). The alternative mode
"aspectj" will instead weave the affected classes with
Spring's AspectJ transaction aspect (modifying the target
class byte code in order to apply to any kind of method call).
AspectJ weaving requires spring-aspects.jar on the classpath
as well as load-time weaving (or compile-time weaving)
enabled. (See the section entitled <xref
linkend="aop-aj-ltw-spring" /> for details on how to set up
load-time weaving.)</para></entry>
</row>
<row>
<entry><literal>proxy-target-class</literal></entry>
<entry>false</entry>
<entry><para>Applies to proxy mode only. Controls what type of
transactional proxies are created for classes annotated with
the <interfacename>@Transactional</interfacename> annotation.
If "<literal>proxy-target-class</literal>" attribute is set to
"<literal>true</literal>", then class-based proxies will be
created. If "<literal>proxy-target-class</literal>" is
"<literal>false</literal>" or if the attribute is omitted,
then standard JDK interface-based proxies will be created.
(See the section entitled <xref linkend="aop-proxying" /> for
a detailed examination of the different proxy
types.)</para></entry>
</row>
<row>
<entry><literal>order</literal></entry>
<entry>Ordered.LOWEST_PRECEDENCE</entry>
<entry><para>Defines the order of the transaction advice that
will be applied to beans annotated with
<interfacename>@Transactional</interfacename>. More on the
rules related to ordering of AOP advice can be found in the
AOP chapter (see section <xref
linkend="aop-ataspectj-advice-ordering" />). Note that not
specifying any ordering will leave the decision as to what
order advice is run in to the AOP subsystem.</para></entry>
</row>
</tbody>
</tgroup>
</table></para>
<note>
<para>The "<literal>proxy-target-class</literal>" attribute on the
<literal>&lt;tx:annotation-driven/&gt;</literal> element controls what
type of transactional proxies are created for classes annotated with
the <interfacename>@Transactional</interfacename> annotation. If
"<literal>proxy-target-class</literal>" attribute is set to
"<literal>true</literal>", then class-based proxies will be created.
If "<literal>proxy-target-class</literal>" is
"<literal>false</literal>" or if the attribute is omitted, then
standard JDK interface-based proxies will be created. (See the section
entitled <xref linkend="aop-proxying" /> for a detailed examination of
the different proxy types.)</para>
</note>
<note>
<para>Note that <literal>&lt;tx:annotation-driven/&gt;</literal> only
looks for <interfacename>@Transactional</interfacename> on beans in
the same application context it is defined in. This means that, if you
put <literal>&lt;tx:annotation-driven/&gt;</literal> in a
<interfacename>WebApplicationContext</interfacename> for a
<classname>DispatcherServlet</classname>, it only checks for
<interfacename>@Transactional</interfacename> beans in your
controllers, and not your services. See <xref linkend="mvc-servlet" />
for more information.</para>
</note>
<para>The most derived location takes precedence when evaluating the
transactional settings for a method. In the case of the following
example, the <classname>DefaultFooService</classname> class is annotated
at the class level with the settings for a read-only transaction, but
the <interfacename>@Transactional</interfacename> annotation on the
<methodname>updateFoo(Foo)</methodname> method in the same class takes
precedence over the transactional settings defined at the class
level.</para>
<programlisting language="java">@Transactional(readOnly = true)
public class DefaultFooService implements FooService {
public Foo getFoo(String fooName) {
<lineannotation>// do something</lineannotation>
}
<lineannotation>// <emphasis role="bold">these</emphasis> settings have precedence for this method</lineannotation>
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateFoo(Foo foo) {
<lineannotation>// do something</lineannotation>
}
}</programlisting>
<section id="transaction-declarative-attransactional-settings">
<title><interfacename>@Transactional</interfacename> settings</title>
<para>The <interfacename>@Transactional</interfacename> annotation is
metadata that specifies that an interface, class, or method must have
transactional semantics; for example, <quote><emphasis>start a brand
new read-only transaction when this method is invoked, suspending any
existing transaction</emphasis></quote>. The default
<interfacename>@Transactional</interfacename> settings are:</para>
<itemizedlist>
<listitem>
<para>The propagation setting is
<literal>PROPAGATION_REQUIRED</literal></para>
</listitem>
<listitem>
<para>The isolation level is
<literal>ISOLATION_DEFAULT</literal></para>
</listitem>
<listitem>
<para>The transaction is read/write</para>
</listitem>
<listitem>
<para>The transaction timeout defaults to the default timeout of
the underlying transaction system, or or none if timeouts are not
supported</para>
</listitem>
<listitem>
<para>Any <exceptionname>RuntimeException</exceptionname> will
trigger rollback, and any checked
<exceptionname>Exception</exceptionname> will not</para>
</listitem>
</itemizedlist>
<para>These default settings can be changed; the various properties of
the <interfacename>@Transactional</interfacename> annotation are
summarized in the following table:</para>
<para><table id="tx-attransactional-properties">
<title><interfacename>@Transactional</interfacename>
properties</title>
<tgroup cols="3">
<thead>
<row>
<entry>Property</entry>
<entry>Type</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal><link
linkend="tx-propagation">propagation</link></literal></entry>
<entry>enum: <classname>Propagation</classname></entry>
<entry>optional propagation setting</entry>
</row>
<row>
<entry><literal>isolation</literal></entry>
<entry>enum: <classname>Isolation</classname></entry>
<entry>optional isolation level</entry>
</row>
<row>
<entry><literal>readOnly</literal></entry>
<entry>boolean</entry>
<entry>read/write vs. read-only transaction</entry>
</row>
<row>
<entry><literal>timeout</literal></entry>
<entry>int (in seconds granularity)</entry>
<entry>the transaction timeout</entry>
</row>
<row>
<entry><literal>rollbackFor</literal></entry>
<entry>an array of <classname>Class</classname> objects,
which must be derived from
<classname>Throwable</classname></entry>
<entry>an optional array of exception classes which
<emphasis role="bold">must</emphasis> cause rollback</entry>
</row>
<row>
<entry><literal>rollbackForClassname</literal></entry>
<entry>an array of <classname></classname> class names.
Classes must be derived from
<classname>Throwable</classname></entry>
<entry>an optional array of names of exception classes that
<emphasis role="bold">must</emphasis> cause rollback</entry>
</row>
<row>
<entry><literal>noRollbackFor</literal></entry>
<entry>an array of <classname>Class</classname> objects,
which must be derived from
<classname>Throwable</classname></entry>
<entry>an optional array of exception classes that <emphasis
role="bold">must not</emphasis> cause rollback.</entry>
</row>
<row>
<entry><literal>noRollbackForClassname</literal></entry>
<entry>an array of <classname>String</classname> class
names, which must be derived from
<classname>Throwable</classname></entry>
<entry>an optional array of names of exception classes that
<emphasis role="bold">must not</emphasis> cause
rollback</entry>
</row>
</tbody>
</tgroup>
</table></para>
<para>Currently it is not possible to have explicit control over the
name of a transaction, where 'name' means the transaction name that
will be shown in a transaction monitor, if applicable (for example,
WebLogic's transaction monitor), and in logging output. For
declarative transactions, the transaction name is always the
fully-qualified class name + "." + method name of the
transactionally-advised class. For example, if the
<methodname>handlePayment(..)</methodname> method of the
<classname>BusinessService</classname> class started a transaction,
the name of the transaction would be:
<literal>com.foo.BusinessService.handlePayment</literal>.</para>
</section>
</section>
<section id="tx-propagation">
<title>Transaction propagation</title>
<para><emphasis>Please note that this section of the Spring reference
documentation is <emphasis>not</emphasis> an introduction to transaction
propagation proper; rather it details some of the semantics regarding
transaction propagation in Spring.</emphasis></para>
<para>In the case of Spring-managed transactions, please be aware of the
difference between <emphasis>physical</emphasis> and
<emphasis>logical</emphasis> transactions, and how the propagation
setting applies to this difference.</para>
<section id="tx-propagation-required">
<title>Required</title>
<para><mediaobject>
<imageobject>
<imagedata align="center" fileref="images/tx_prop_required.png"
format="PNG" />
</imageobject>
<caption><para>PROPAGATION_REQUIRED</para></caption>
</mediaobject></para>
<para>When the propagation setting is
<literal>PROPAGATION_REQUIRED</literal>, a
<emphasis>logical</emphasis> transaction scope is created for each
method that it gets applied to. Each such logical transaction scope
can individually decide on rollback-only status, with an outer
transaction scope being logically independent from the inner
transaction scope. Of course, in case of standard
<literal>PROPAGATION_REQUIRED</literal> behavior, they will be mapped
to the same physical transaction. So a rollback-only marker set in the
inner transaction scope does affect the outer transactions chance to
actually commit (as you would expect it to).</para>
<para>However, in the case where an inner transaction scopes sets the
rollback-only marker, the outer transaction itself has not decided on
the rollback itself, and so the rollback (silently triggered by the
inner transaction scope) is unexpected: a corresponding
<classname>UnexpectedRollbackException</classname> will be thrown at
that point. This is <emphasis>expected behavior</emphasis> so that the
caller of a transaction can never be misled to assume that a commit
was performed when it really was not. So if an inner transaction (that
the outer caller is not aware of) silently marks a transaction as
rollback-only, the outer caller would still innocently call commit -
and needs to receive an
<classname>UnexpectedRollbackException</classname> to indicate clearly
that a rollback was performed instead.</para>
</section>
<section id="tx-propagation-requires_new">
<title>RequiresNew</title>
<para><mediaobject>
<imageobject>
<imagedata align="center"
fileref="images/tx_prop_requires_new.png"
format="PNG" />
</imageobject>
<caption><para>PROPAGATION_REQUIRES_NEW</para></caption>
</mediaobject></para>
<para><literal>PROPAGATION_REQUIRES_NEW</literal>, in contrast, uses a
<emphasis>completely</emphasis> independent transaction for each
affected transaction scope. In that case, the underlying physical
transactions will be different and hence can commit or rollback
independently, with an outer transaction not affected by an inner
transaction's rollback status.</para>
</section>
<section id="tx-propagation-nested">
<title>Nested</title>
<para><literal>PROPAGATION_NESTED</literal> is different again in that
it uses a <emphasis>single</emphasis> physical transaction with
multiple savepoints that it can roll back to. Such partial rollbacks
allow an inner transaction scope to trigger a rollback <emphasis>for
its scope</emphasis>, with the outer transaction being able to
continue the physical transaction despite some operations having been
rolled back. This is typically mapped onto JDBC savepoints, so will
only work with JDBC resource transactions (see Spring's
<classname>DataSourceTransactionManager</classname>).</para>
</section>
</section>
<section id="transaction-declarative-applying-more-than-just-tx-advice">
<title>Advising transactional operations</title>
<para>Consider the situation where you would like to execute
<emphasis>both</emphasis> transactional <emphasis>and</emphasis> (to
keep things simple) some basic profiling advice. How do you effect this
in the context of using
<literal>&lt;tx:annotation-driven/&gt;</literal>?</para>
<para>What we want to see when we invoke the
<methodname>updateFoo(Foo)</methodname> method is:</para>
<itemizedlist>
<listitem>
<para>the configured profiling aspect starting up,</para>
</listitem>
<listitem>
<para>then the transactional advice executing,</para>
</listitem>
<listitem>
<para>then the method on the advised object executing</para>
</listitem>
<listitem>
<para>then the transaction committing (we'll assume a sunny day
scenario here),</para>
</listitem>
<listitem>
<para>and then finally the profiling aspect reporting (somehow)
exactly how long the whole transactional method invocation
took</para>
</listitem>
</itemizedlist>
<note>
<para>This chapter is not concerned with explaining AOP in any great
detail (except as it applies to transactions). Please see the chapter
entitled <xref linkend="aop" /> for detailed coverage of the various
bits and pieces of the following AOP configuration (and AOP in
general).</para>
</note>
<para>Here is the code for a simple profiling aspect. The ordering of
advice is controlled via the <interfacename>Ordered</interfacename>
interface. For full details on advice ordering, see <xref
linkend="aop-ataspectj-advice-ordering" />.</para>
<programlisting language="java">package x.y;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.util.StopWatch;
import org.springframework.core.Ordered;
public class SimpleProfiler implements Ordered {
private int order;
<lineannotation>// allows us to control the ordering of advice</lineannotation>
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
<lineannotation>// this method <emphasis>is</emphasis> the around advice</lineannotation>
public Object profile(ProceedingJoinPoint call) throws Throwable {
Object returnValue;
StopWatch clock = new StopWatch(getClass().getName());
try {
clock.start(call.toShortString());
returnValue = call.proceed();
} finally {
clock.stop();
System.out.println(clock.prettyPrint());
}
return returnValue;
}
}
</programlisting>
<programlisting language="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
2009-05-16 04:15:18 +08:00
http://www.springframework.org/schema/beans
2009-06-10 07:39:37 +08:00
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
2009-05-16 04:15:18 +08:00
http://www.springframework.org/schema/tx
2009-06-10 07:39:37 +08:00
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
2009-05-16 04:15:18 +08:00
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"&gt;
&lt;bean id="fooService" class="x.y.service.DefaultFooService"/&gt;
<lineannotation>&lt;!-- this is the aspect --&gt;</lineannotation>
&lt;bean id="profiler" class="x.y.SimpleProfiler"&gt;
<lineannotation>&lt;!-- execute before the transactional advice (hence the lower order number) --&gt;</lineannotation>
&lt;property name="order" <emphasis role="bold">value="1"</emphasis>/&gt;
&lt;/bean&gt;
&lt;tx:annotation-driven transaction-manager="txManager" <emphasis
role="bold">order="200"</emphasis>/&gt;
&lt;aop:config&gt;
<lineannotation>&lt;!-- this advice will execute <emphasis role="bold">around</emphasis> the transactional advice --&gt;</lineannotation>
&lt;aop:aspect id="profilingAspect" ref="profiler"&gt;
&lt;aop:pointcut id="serviceMethodWithReturnValue"
expression="execution(!void x.y..*Service.*(..))"/&gt;
&lt;aop:around method="profile" pointcut-ref="serviceMethodWithReturnValue"/&gt;
&lt;/aop:aspect&gt;
&lt;/aop:config&gt;
&lt;bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"&gt;
&lt;property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/&gt;
&lt;property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/&gt;
&lt;property name="username" value="scott"/&gt;
&lt;property name="password" value="tiger"/&gt;
&lt;/bean&gt;
&lt;bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"&gt;
&lt;property name="dataSource" ref="dataSource"/&gt;
&lt;/bean&gt;
&lt;/beans&gt;</programlisting>
<para>The result of the above configuration will be a
<literal>'fooService'</literal> bean that has profiling and
transactional aspects applied to it <emphasis>in that order</emphasis>.
The configuration of any number of additional aspects is effected in a
similar fashion.</para>
<para>Finally, find below some example configuration for effecting the
same setup as above, but using the purely XML declarative
approach.</para>
<programlisting language="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
2009-05-16 04:15:18 +08:00
http://www.springframework.org/schema/beans
2009-06-10 07:39:37 +08:00
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
2009-05-16 04:15:18 +08:00
http://www.springframework.org/schema/tx
2009-06-10 07:39:37 +08:00
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
2009-05-16 04:15:18 +08:00
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"&gt;
&lt;bean id="fooService" class="x.y.service.DefaultFooService"/&gt;
<lineannotation>&lt;!-- the profiling advice --&gt;</lineannotation>
&lt;bean id="profiler" class="x.y.SimpleProfiler"&gt;
<lineannotation>&lt;!-- execute before the transactional advice (hence the lower order number) --&gt;</lineannotation>
<emphasis role="bold">&lt;property name="order" value="1</emphasis>"/&gt;
&lt;/bean&gt;
&lt;aop:config&gt;
&lt;aop:pointcut id="entryPointMethod" expression="execution(* x.y..*Service.*(..))"/&gt;
<lineannotation>&lt;!-- will execute after the profiling advice (c.f. the order attribute) --&gt;</lineannotation>
&lt;aop:advisor
advice-ref="txAdvice"
pointcut-ref="entryPointMethod"
<emphasis role="bold">order="2</emphasis>"/&gt; <lineannotation>&lt;!-- order value is higher than the profiling aspect --&gt;</lineannotation>
&lt;aop:aspect id="profilingAspect" ref="profiler"&gt;
&lt;aop:pointcut id="serviceMethodWithReturnValue"
expression="execution(!void x.y..*Service.*(..))"/&gt;
&lt;aop:around method="profile" pointcut-ref="serviceMethodWithReturnValue"/&gt;
&lt;/aop:aspect&gt;
&lt;/aop:config&gt;
&lt;tx:advice id="txAdvice" transaction-manager="txManager"&gt;
&lt;tx:attributes&gt;
&lt;tx:method name="get*" read-only="true"/&gt;
&lt;tx:method name="*"/&gt;
&lt;/tx:attributes&gt;
&lt;/tx:advice&gt;
<lineannotation>&lt;!-- other &lt;bean/&gt; definitions such as a <interfacename>DataSource</interfacename> and a <interfacename>PlatformTransactionManager</interfacename> here --&gt;</lineannotation>
&lt;/beans&gt;</programlisting>
<para>The result of the above configuration will be a
<literal>'fooService'</literal> bean that has profiling and
transactional aspects applied to it <emphasis>in that order</emphasis>.
If we wanted the profiling advice to execute <emphasis>after</emphasis>
the transactional advice on the way in, and <emphasis>before</emphasis>
the transactional advice on the way out, then we would simply swap the
value of the profiling aspect bean's <literal>'order'</literal> property
such that it was higher than the transactional advice's order
value.</para>
<para>The configuration of any number of additional aspects is achieved
in a similar fashion.</para>
</section>
<section id="transaction-declarative-aspectj">
<title>Using <interfacename>@Transactional</interfacename> with
AspectJ</title>
<para>It is also possible to use the Spring Framework's
<interfacename>@Transactional</interfacename> support outside of a
Spring container by means of an AspectJ aspect. To use this support you
must first annotate your classes (and optionally your classes' methods
with the <interfacename>@Transactional</interfacename> annotation, and
then you must link (weave) your application with the
<classname>org.springframework.transaction.aspectj.AnnotationTransactionAspect</classname>
defined in the <filename
class="libraryfile">spring-aspects.jar</filename> file. The aspect must
also be configured with a transaction manager. You could of course use
the Spring Framework's IoC container to take care of dependency
injecting the aspect. The simplest way to configure the transaction
management aspect is to use the
<literal>'&lt;tx:annotation-driven/&gt;'</literal> element and specify
the <literal>mode</literal> attribute to <literal>asepctj</literal> as
described in <xref linkend="transaction-declarative-annotations" />.
Since we're focusing here on applications running outside of a Spring
container, we'll show you how to do it programmatically.</para>
<note>
<para>Prior to continuing, you may well want to read the previous
sections entitled <xref
linkend="transaction-declarative-annotations" /> and <xref
linkend="aop" /> respectively.</para>
</note>
<programlisting language="java"><lineannotation>// construct an appropriate transaction manager </lineannotation>
DataSourceTransactionManager txManager = new DataSourceTransactionManager(getDataSource());
<lineannotation>// configure the <classname>AnnotationTransactionAspect</classname> to use it; this must be done before executing any transactional methods</lineannotation>
AnnotationTransactionAspect.aspectOf().setTransactionManager(txManager); </programlisting>
<note>
<para>When using this aspect, you must annotate the
<emphasis>implementation</emphasis> class (and/or methods within that
class), <emphasis>not</emphasis> the interface (if any) that the class
implements. AspectJ follows Java's rule that annotations on interfaces
are <emphasis>not inherited</emphasis>.</para>
</note>
<para>The <interfacename>@Transactional</interfacename> annotation on a
class specifies the default transaction semantics for the execution of
any method in the class.</para>
<para>The <interfacename>@Transactional</interfacename> annotation on a
method within the class overrides the default transaction semantics
given by the class annotation (if present). Any method may be annotated,
regardless of visibility.</para>
<para>To weave your applications with the
<classname>AnnotationTransactionAspect</classname> you must either build
your application with AspectJ (see the <ulink
url="http://www.eclipse.org/aspectj/doc/released/devguide/index.html">AspectJ
Development Guide</ulink>) or use load-time weaving. See the section
entitled <xref linkend="aop-aj-ltw" /> for a discussion of load-time
weaving with AspectJ.</para>
</section>
</section>
<section id="transaction-programmatic">
<title>Programmatic transaction management</title>
<para>The Spring Framework provides two means of programmatic transaction
management:</para>
<itemizedlist>
<listitem>
<para>Using the <classname>TransactionTemplate</classname>.</para>
</listitem>
<listitem>
<para>Using a
<interfacename>PlatformTransactionManager</interfacename>
implementation directly.</para>
</listitem>
</itemizedlist>
<para>If you are going to use programmatic transaction management, the
Spring team generally recommends using the
<classname>TransactionTemplate</classname>. The second approach is similar
to using the JTA <interfacename>UserTransaction</interfacename> API
(although exception handling is less cumbersome).</para>
<section id="tx-prog-template">
<title>Using the <classname>TransactionTemplate</classname></title>
<para>The <classname>TransactionTemplate</classname> adopts the same
approach as other Spring <emphasis>templates</emphasis> such as the
<classname>JdbcTemplate</classname>. It uses a callback approach, to
free application code from having to do the boilerplate acquisition and
release of transactional resources, and results in code that is
intention driven, in that the code that is written focuses solely on
what the developer wants to do.</para>
<note>
<para>As you will immediately see in the examples that follow, using
the <classname>TransactionTemplate</classname> absolutely couples you
to Spring's transaction infrastructure and APIs. Whether or not
programmatic transaction management is suitable for your development
needs is a decision that you will have to make yourself.</para>
</note>
<para>Application code that must execute in a transactional context, and
that will use the <classname>TransactionTemplate</classname> explicitly,
looks like this. You, as an application developer, will write a
<interfacename>TransactionCallback</interfacename> implementation
(typically expressed as an anonymous inner class) that will contain all
of the code that you need to have execute in the context of a
transaction. You will then pass an instance of your custom
<interfacename>TransactionCallback</interfacename> to the
<methodname>execute(..)</methodname> method exposed on the
<classname>TransactionTemplate</classname>.</para>
<programlisting language="java">public class SimpleService implements Service {
<lineannotation>// single <classname>TransactionTemplate</classname> shared amongst all methods in this instance</lineannotation>
private final TransactionTemplate transactionTemplate;
<lineannotation>// use constructor-injection to supply the <interfacename>PlatformTransactionManager</interfacename></lineannotation>
public SimpleService(PlatformTransactionManager transactionManager) {
Assert.notNull(transactionManager, "The 'transactionManager' argument must not be null.");
this.transactionTemplate = new TransactionTemplate(transactionManager);
}
public Object someServiceMethod() {
return transactionTemplate.execute(new TransactionCallback() {
<lineannotation>// the code in this method executes in a transactional context</lineannotation>
public Object doInTransaction(TransactionStatus status) {
updateOperation1();
return resultOfUpdateOperation2();
}
});
}
}</programlisting>
<para>If there is no return value, use the convenient
<classname>TransactionCallbackWithoutResult</classname> class via an
anonymous class like so:</para>
<programlisting language="java">transactionTemplate.execute(new <emphasis
role="bold">TransactionCallbackWithoutResult</emphasis>() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
updateOperation1();
updateOperation2();
}
});</programlisting>
<para>Code within the callback can roll the transaction back by calling
the <literal>setRollbackOnly()</literal> method on the supplied
<interfacename>TransactionStatus</interfacename> object.</para>
<programlisting language="java">transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
updateOperation1();
updateOperation2();
} catch (SomeBusinessExeption ex) {
<emphasis role="bold">status.setRollbackOnly();</emphasis>
}
}
});</programlisting>
<section id="tx-prog-template-settings">
<title>Specifying transaction settings</title>
<para>Transaction settings such as the propagation mode, the isolation
level, the timeout, and so forth can be set on the
<classname>TransactionTemplate</classname> either programmatically or
in configuration. <classname>TransactionTemplate</classname> instances
by default have the <link
linkend="transaction-declarative-txadvice-settings">default
transactional settings</link>. Find below an example of
programmatically customizing the transactional settings for a specific
<classname>TransactionTemplate</classname>.</para>
<programlisting language="java">public class SimpleService implements Service {
private final TransactionTemplate transactionTemplate;
public SimpleService(PlatformTransactionManager transactionManager) {
Assert.notNull(transactionManager, "The 'transactionManager' argument must not be null.");
this.transactionTemplate = new TransactionTemplate(transactionManager);
<lineannotation>// the transaction settings can be set here explicitly if so desired</lineannotation>
this.transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
this.transactionTemplate.setTimeout(30); <lineannotation>// 30 seconds</lineannotation>
<lineannotation>// and so forth...</lineannotation>
}
}</programlisting>
<para>Find below an example of defining a
<classname>TransactionTemplate</classname> with some custom
transactional settings, using Spring XML configuration. The
'<literal>sharedTransactionTemplate</literal>' can then be injected
into as many services as are required.</para>
<programlisting language="xml">&lt;bean id="sharedTransactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate"&gt;
&lt;property name="isolationLevelName" value="ISOLATION_READ_UNCOMMITTED"/&gt;
&lt;property name="timeout" value="30"/&gt;
&lt;/bean&gt;"</programlisting>
</section>
<para>Finally, instances of the
<classname>TransactionTemplate</classname> class are threadsafe, in that
instances do not maintain any conversational state.
<classname>TransactionTemplate</classname> instances
<emphasis>do</emphasis> however maintain configuration state, so while a
number of classes may choose to share a single instance of a
<classname>TransactionTemplate</classname>, if a class needed to use a
<classname>TransactionTemplate</classname> with different settings (for
example, a different isolation level), then two distinct
<classname>TransactionTemplate</classname> instances would need to be
created and used.</para>
</section>
<section id="transaction-programmatic-ptm">
<title>Using the
<interfacename>PlatformTransactionManager</interfacename></title>
<para>You can also use the
<interfacename>org.springframework.transaction.PlatformTransactionManager</interfacename>
directly to manage your transaction. Simply pass the implementation of
the <interfacename>PlatformTransactionManager</interfacename> you're
using to your bean via a bean reference. Then, using the
<interfacename>TransactionDefinition</interfacename> and
<interfacename>TransactionStatus</interfacename> objects you can
initiate transactions, rollback and commit.</para>
<programlisting language="java">DefaultTransactionDefinition def = new DefaultTransactionDefinition();
<lineannotation>// explicitly setting the transaction name is something that can only be done programmatically</lineannotation>
def.setName("SomeTxName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
try {
<lineannotation>// execute your business logic here</lineannotation>
}
catch (MyException ex) {
txManager.rollback(status);
throw ex;
}
txManager.commit(status);</programlisting>
</section>
</section>
<section id="tx-decl-vs-prog">
<title>Choosing between programmatic and declarative transaction
management</title>
<para>Programmatic transaction management is usually a good idea only if
you have a small number of transactional operations. For example, if you
have a web application that require transactions only for certain update
operations, you may not want to set up transactional proxies using Spring
or any other technology. In this case, using the
<classname>TransactionTemplate</classname> <emphasis>may</emphasis> be a
good approach. Being able to set the transaction name explicitly is also
something that can only be done using the programmatic approach to
transaction management.</para>
<para>On the other hand, if your application has numerous transactional
operations, declarative transaction management is usually worthwhile. It
keeps transaction management out of business logic, and is not difficult
to configure. When using the Spring Framework, rather than EJB CMT, the
configuration cost of declarative transaction management is greatly
reduced.</para>
</section>
<section id="transaction-application-server-integration">
<title>Application server-specific integration</title>
<para>Spring's transaction abstraction generally is application server
agnostic. Additionally, Spring's
<classname>JtaTransactionManager</classname> class, which can optionally
perform a JNDI lookup for the JTA
<interfacename>UserTransaction</interfacename> and
<interfacename>TransactionManager</interfacename> objects, autodetects the
location for the latter object, which varies by application server. Having
access to the JTA <interfacename>TransactionManager</interfacename> allows
for enhanced transaction semantics, in particular supporting transaction
suspension. Please see the <classname>JtaTransactionManager</classname>
Javadocs for details.</para>
<para>Spring's <classname>JtaTransactionManager</classname> is the
standard choice when running on J2EE application servers, known to work on
all common servers. Its advanced functionality such as transaction
suspension is known to work on many servers as well - including GlassFish,
JBoss, Geronimo and Oracle OC4J - without any special configuration
required. However, for fully supported transaction suspension and further
advanced integration, Spring ships special adapters for IBM WebSphere and
BEA WebLogic and also for Oracle OC4J. We'll discuss these adapters in the
following sections.</para>
<para><emphasis>For standard scenarios, including WebLogic, WebSphere and
OC4J, consider using the convenient
<literal>'&lt;tx:jta-transaction-manager/&gt;'</literal> configuration
element.</emphasis> This will automatically detect the underlying server
and choose the best transaction manager available for the platform. This
means that you won't have to configure server-specific adapter classes (as
discussed in the following sections) explicitly; they will rather be
chosen automatically, with the standard
<classname>JtaTransactionManager</classname> as default fallback.</para>
<section id="transaction-application-server-integration-websphere">
<title>IBM WebSphere</title>
<para>On WebSphere 6.1.0.9 and above, the recommended Spring JTA
transaction manager to use is
<classname>WebSphereUowTransactionManager</classname>. This special
adapter leverages IBM's <interfacename>UOWManager</interfacename> API
which is available in WebSphere Application Server 6.0.2.19 or above and
6.1.0.9 or above. With this adapter, Spring-driven transaction
suspension (suspend/resume as initiated by
<literal>PROPAGATION_REQUIRES_NEW</literal>) is officially supported by
IBM!</para>
</section>
<section id="transaction-application-server-integration-weblogic">
<title>BEA WebLogic</title>
<para>On WebLogic 9.0 or above, you will generally prefer to use the
<classname>WebLogicJtaTransactionManager</classname> instead of the
stock <classname>JtaTransactionManager</classname> class. This special
WebLogic-specific subclass of the normal
<classname>JtaTransactionManager</classname> supports the full power of
Spring's transaction definitions in a WebLogic-managed transaction
environment, beyond standard JTA semantics: Features include transaction
names, per-transaction isolation levels, and proper resuming of
transactions in all cases.</para>
</section>
<section id="transaction-application-server-integration-oc4j">
<title>Oracle OC4J</title>
<para>Spring ships a special adapter class for OC4J 10.1.3 or above:
<classname>OC4JJtaTransactionManager</classname>. This is analogous to
the <classname>WebLogicJtaTransactionManager</classname> class discussed
in the previous section, providing similar value-adds on OC4J:
transaction names and per-transaction isolation levels.</para>
<para>Note that the full JTA functionality, including transaction
suspension, works fine with Spring's
<classname>JtaTransactionManager</classname> on OC4J as well. The
special <classname>OC4JJtaTransactionManager</classname> adapter simply
provides value-adds beyond standard JTA.</para>
</section>
</section>
<section id="transaction-solutions-to-common-problems">
<title>Solutions to common problems</title>
<section id="transaction-solutions-to-common-problems-wrong-ptm">
<title>Use of the wrong transaction manager for a specific
<interfacename>DataSource</interfacename></title>
<para>You should take care to use the <emphasis>correct</emphasis>
<interfacename>PlatformTransactionManager</interfacename> implementation
for their requirements. Used properly, the Spring Framework merely
provides a straightforward and portable abstraction. If you are using
global transactions, you <emphasis>must</emphasis> use the
<classname>org.springframework.transaction.jta.JtaTransactionManager</classname>
class (or an <link
linkend="transaction-application-server-integration">application
server-specific subclass</link> of it) for all your transactional
operations. Otherwise the transaction infrastructure will attempt to
perform local transactions on resources such as container
<interfacename>DataSource</interfacename> instances. Such local
transactions do not make sense, and a good application server will treat
them as errors.</para>
</section>
</section>
<section id="transaction-resources">
<title>Further Resources</title>
<para>Find below links to further resources about the Spring Framework's
transaction support.</para>
<itemizedlist>
<listitem>
<para><ulink
url="http://www.javaworld.com/javaworld/jw-01-2009/jw-01-spring-transactions.html">Distributed
transactions in Spring, with and without XA</ulink> is a JavaWorld
feature where SpringSource's David Syer guides you through seven
patterns for distributed transactions in Spring applications, three of
them with XA and four without.</para>
</listitem>
<listitem>
<para><ulink url="http://www.infoq.com/minibooks/JTDS">Java
Transaction Design Strategies</ulink> is a book available from <ulink
url="http://www.infoq.com/">InfoQ</ulink> that provides a well-paced
introduction to transactions in Java. It also includes side-by-side
examples of how to configure and use transactions using both the
Spring Framework and EJB3.</para>
</listitem>
</itemizedlist>
</section>
</chapter>